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 +4 -4
- data/CHANGELOG.md +6 -0
- data/ext/oj/custom.c +10 -9
- data/ext/oj/dump.c +319 -20
- data/ext/oj/dump.h +7 -2
- data/ext/oj/dump_compat.c +9 -8
- data/ext/oj/dump_leaf.c +1 -1
- data/ext/oj/dump_object.c +27 -17
- data/ext/oj/dump_strict.c +7 -6
- data/ext/oj/fast.c +1 -1
- data/ext/oj/object.c +8 -8
- data/ext/oj/oj.c +10 -5
- data/ext/oj/rails.c +19 -17
- data/ext/oj/simd.h +10 -0
- data/ext/oj/wab.c +4 -3
- data/lib/oj/version.rb +1 -1
- data/pages/Encoding.md +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 30aea721380a4e3edc306dd19906d8777f230a639ba3427e9394dd543a3a7e3b
|
4
|
+
data.tar.gz: b024a9d4513c16c1bfe4fc3c4adeacb1afd9a0e670987476d32cfa2fa74e9b1e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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),
|
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
|
-
|
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
|
-
|
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 =
|
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),
|
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
|
-
|
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 =
|
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 =
|
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,
|
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),
|
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),
|
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),
|
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
|
763
|
-
char
|
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
|
-
|
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
|
-
|
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;
|
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
|
-
|
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)
|
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),
|
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
|
-
|
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
|
-
|
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 =
|
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
|
-
|
1243
|
-
|
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 =
|
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,
|
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
|
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
|
-
|
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 =
|
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
|
-
|
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 =
|
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
|
-
|
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 =
|
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
|
-
|
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 =
|
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),
|
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
|
-
|
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
|
-
|
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
|
-
|
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 =
|
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 =
|
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),
|
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
|
-
|
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),
|
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
|
-
|
568
|
-
int d2
|
569
|
-
int d3
|
570
|
-
size_t len
|
571
|
-
size_t size
|
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
|
-
|
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 =
|
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
|
-
|
617
|
-
|
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
|
-
|
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 =
|
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
|
-
|
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 =
|
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),
|
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),
|
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 =
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
927
|
+
size_t cnt;
|
927
928
|
|
928
929
|
rb_check_type(v, T_ARRAY);
|
929
|
-
cnt =
|
930
|
+
cnt = RARRAY_LEN(v);
|
930
931
|
if (0 < cnt) {
|
931
|
-
|
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 <
|
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
|
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
|
-
|
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 =
|
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,
|
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,
|
210
|
+
oj_dump_raw(str, RSTRING_LEN(rstr), out);
|
211
211
|
} else {
|
212
|
-
oj_dump_cstr(str,
|
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),
|
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
|
-
|
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
|
-
|
356
|
-
|
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 =
|
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
|
-
|
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 =
|
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
|
-
|
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 =
|
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
|
-
|
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 =
|
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
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
|
-
|
114
|
+
size_t i;
|
115
|
+
size_t cnt;
|
115
116
|
int d2 = depth + 1;
|
116
117
|
|
117
|
-
cnt =
|
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),
|
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
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
|
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.
|
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-
|
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
|