oj 3.12.1 → 3.13.1
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/README.md +1 -2
- data/ext/oj/buf.h +9 -0
- data/ext/oj/cache.c +187 -0
- data/ext/oj/cache.h +20 -0
- data/ext/oj/compat.c +8 -22
- data/ext/oj/custom.c +14 -13
- data/ext/oj/debug.c +131 -0
- data/ext/oj/dump.c +50 -56
- data/ext/oj/dump_compat.c +3 -3
- data/ext/oj/dump_object.c +9 -9
- data/ext/oj/dump_strict.c +3 -3
- data/ext/oj/err.h +19 -0
- data/ext/oj/extconf.rb +4 -0
- data/ext/oj/fast.c +6 -17
- data/ext/oj/intern.c +398 -0
- data/ext/oj/intern.h +27 -0
- data/ext/oj/mimic_json.c +9 -9
- data/ext/oj/object.c +10 -58
- data/ext/oj/odd.c +1 -1
- data/ext/oj/oj.c +164 -106
- data/ext/oj/oj.h +2 -2
- data/ext/oj/parse.c +4 -4
- data/ext/oj/parser.c +1511 -0
- data/ext/oj/parser.h +90 -0
- data/ext/oj/rails.c +5 -5
- data/ext/oj/resolve.c +2 -20
- data/ext/oj/saj2.c +346 -0
- data/ext/oj/scp.c +1 -1
- data/ext/oj/sparse.c +1 -1
- data/ext/oj/stream_writer.c +3 -3
- data/ext/oj/strict.c +10 -27
- data/ext/oj/usual.c +1222 -0
- data/ext/oj/validate.c +50 -0
- data/ext/oj/wab.c +9 -17
- data/lib/oj/mimic.rb +1 -1
- data/lib/oj/version.rb +1 -1
- data/pages/Modes.md +2 -0
- data/pages/Options.md +23 -5
- data/pages/Parser.md +309 -0
- data/test/foo.rb +2 -9
- data/test/perf_parser.rb +184 -0
- data/test/test_parser.rb +27 -0
- data/test/test_parser_saj.rb +245 -0
- data/test/test_parser_usual.rb +213 -0
- metadata +23 -6
- data/ext/oj/hash.c +0 -168
- data/ext/oj/hash.h +0 -21
- data/ext/oj/hash_test.c +0 -491
data/ext/oj/dump.c
CHANGED
@@ -472,7 +472,7 @@ void oj_dump_time(VALUE obj, Out out, int withZone) {
|
|
472
472
|
void oj_dump_ruby_time(VALUE obj, Out out) {
|
473
473
|
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
474
474
|
|
475
|
-
oj_dump_cstr(
|
475
|
+
oj_dump_cstr(RSTRING_PTR(rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
|
476
476
|
}
|
477
477
|
|
478
478
|
void oj_dump_xml_time(VALUE obj, Out out) {
|
@@ -535,59 +535,57 @@ void oj_dump_xml_time(VALUE obj, Out out) {
|
|
535
535
|
}
|
536
536
|
if ((0 == nsec && !out->opts->sec_prec_set) || 0 == out->opts->sec_prec) {
|
537
537
|
if (0 == tzsecs && rb_funcall2(obj, oj_utcq_id, 0, 0)) {
|
538
|
-
sprintf(buf,
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
oj_dump_cstr(buf,
|
538
|
+
int len = sprintf(buf,
|
539
|
+
"%04d-%02d-%02dT%02d:%02d:%02dZ",
|
540
|
+
ti.year,
|
541
|
+
ti.mon,
|
542
|
+
ti.day,
|
543
|
+
ti.hour,
|
544
|
+
ti.min,
|
545
|
+
ti.sec);
|
546
|
+
oj_dump_cstr(buf, len, 0, 0, out);
|
547
547
|
} else {
|
548
|
-
sprintf(buf,
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
oj_dump_cstr(buf,
|
548
|
+
int len = sprintf(buf,
|
549
|
+
"%04d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d",
|
550
|
+
ti.year,
|
551
|
+
ti.mon,
|
552
|
+
ti.day,
|
553
|
+
ti.hour,
|
554
|
+
ti.min,
|
555
|
+
ti.sec,
|
556
|
+
tzsign,
|
557
|
+
tzhour,
|
558
|
+
tzmin);
|
559
|
+
oj_dump_cstr(buf, len, 0, 0, out);
|
560
560
|
}
|
561
561
|
} else if (0 == tzsecs && rb_funcall2(obj, oj_utcq_id, 0, 0)) {
|
562
562
|
char format[64] = "%04d-%02d-%02dT%02d:%02d:%02d.%09ldZ";
|
563
|
-
int len
|
563
|
+
int len;
|
564
564
|
|
565
565
|
if (9 > out->opts->sec_prec) {
|
566
566
|
format[32] = '0' + out->opts->sec_prec;
|
567
|
-
len -= 9 - out->opts->sec_prec;
|
568
567
|
}
|
569
|
-
sprintf(buf, format, ti.year, ti.mon, ti.day, ti.hour, ti.min, ti.sec, (long)nsec);
|
568
|
+
len = sprintf(buf, format, ti.year, ti.mon, ti.day, ti.hour, ti.min, ti.sec, (long)nsec);
|
570
569
|
oj_dump_cstr(buf, len, 0, 0, out);
|
571
570
|
} else {
|
572
571
|
char format[64] = "%04d-%02d-%02dT%02d:%02d:%02d.%09ld%c%02d:%02d";
|
573
|
-
int len
|
572
|
+
int len;
|
574
573
|
|
575
574
|
if (9 > out->opts->sec_prec) {
|
576
575
|
format[32] = '0' + out->opts->sec_prec;
|
577
|
-
len -= 9 - out->opts->sec_prec;
|
578
576
|
}
|
579
|
-
sprintf(buf,
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
577
|
+
len = sprintf(buf,
|
578
|
+
format,
|
579
|
+
ti.year,
|
580
|
+
ti.mon,
|
581
|
+
ti.day,
|
582
|
+
ti.hour,
|
583
|
+
ti.min,
|
584
|
+
ti.sec,
|
585
|
+
(long)nsec,
|
586
|
+
tzsign,
|
587
|
+
tzhour,
|
588
|
+
tzmin);
|
591
589
|
oj_dump_cstr(buf, len, 0, 0, out);
|
592
590
|
}
|
593
591
|
}
|
@@ -710,21 +708,18 @@ void oj_write_obj_to_stream(VALUE obj, VALUE stream, Options copts) {
|
|
710
708
|
}
|
711
709
|
|
712
710
|
void oj_dump_str(VALUE obj, int depth, Out out, bool as_ok) {
|
713
|
-
rb_encoding *enc =
|
711
|
+
rb_encoding *enc = rb_enc_get(obj);
|
714
712
|
|
715
|
-
if (
|
716
|
-
obj = rb_str_conv_enc(obj, enc,
|
713
|
+
if (oj_utf8_encoding != enc) {
|
714
|
+
obj = rb_str_conv_enc(obj, enc, oj_utf8_encoding);
|
717
715
|
}
|
718
|
-
oj_dump_cstr(
|
716
|
+
oj_dump_cstr(RSTRING_PTR(obj), (int)RSTRING_LEN(obj), 0, 0, out);
|
719
717
|
}
|
720
718
|
|
721
719
|
void oj_dump_sym(VALUE obj, int depth, Out out, bool as_ok) {
|
722
|
-
|
723
|
-
// const char *sym = rb_id2name(SYM2ID(obj));
|
720
|
+
volatile VALUE s = rb_sym2str(obj);
|
724
721
|
|
725
|
-
|
726
|
-
|
727
|
-
oj_dump_cstr(rb_string_value_ptr((VALUE *)&s), (int)RSTRING_LEN(s), 0, 0, out);
|
722
|
+
oj_dump_cstr(RSTRING_PTR(s), (int)RSTRING_LEN(s), 0, 0, out);
|
728
723
|
}
|
729
724
|
|
730
725
|
static void debug_raise(const char *orig, size_t cnt, int line) {
|
@@ -762,7 +757,7 @@ void oj_dump_raw_json(VALUE obj, int depth, Out out) {
|
|
762
757
|
if (Yes == out->opts->trace) {
|
763
758
|
oj_trace("raw_json", obj, __FILE__, __LINE__, depth + 1, TraceRubyOut);
|
764
759
|
}
|
765
|
-
oj_dump_raw(
|
760
|
+
oj_dump_raw(RSTRING_PTR(jv), (size_t)RSTRING_LEN(jv), out);
|
766
761
|
}
|
767
762
|
}
|
768
763
|
|
@@ -827,9 +822,8 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
|
|
827
822
|
if (is_sym) {
|
828
823
|
*out->cur++ = ':';
|
829
824
|
}
|
830
|
-
|
831
|
-
|
832
|
-
}
|
825
|
+
memcpy(out->cur, str, cnt);
|
826
|
+
out->cur += cnt;
|
833
827
|
*out->cur++ = '"';
|
834
828
|
} else {
|
835
829
|
const char *end = str + cnt;
|
@@ -961,7 +955,7 @@ void oj_dump_class(VALUE obj, int depth, Out out, bool as_ok) {
|
|
961
955
|
void oj_dump_obj_to_s(VALUE obj, Out out) {
|
962
956
|
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
963
957
|
|
964
|
-
oj_dump_cstr(
|
958
|
+
oj_dump_cstr(RSTRING_PTR(rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
|
965
959
|
}
|
966
960
|
|
967
961
|
void oj_dump_raw(const char *str, size_t cnt, Out out) {
|
@@ -1078,7 +1072,7 @@ void oj_dump_bignum(VALUE obj, int depth, Out out, bool as_ok) {
|
|
1078
1072
|
} else {
|
1079
1073
|
assure_size(out, cnt);
|
1080
1074
|
}
|
1081
|
-
memcpy(out->cur,
|
1075
|
+
memcpy(out->cur, RSTRING_PTR(rs), cnt);
|
1082
1076
|
out->cur += cnt;
|
1083
1077
|
if (dump_as_string) {
|
1084
1078
|
*out->cur++ = '"';
|
@@ -1206,7 +1200,7 @@ void oj_dump_float(VALUE obj, int depth, Out out, bool as_ok) {
|
|
1206
1200
|
if ((int)sizeof(buf) <= cnt) {
|
1207
1201
|
cnt = sizeof(buf) - 1;
|
1208
1202
|
}
|
1209
|
-
|
1203
|
+
memcpy(buf, RSTRING_PTR(rstr), cnt);
|
1210
1204
|
buf[cnt] = '\0';
|
1211
1205
|
} else {
|
1212
1206
|
cnt = oj_dump_float_printf(buf, sizeof(buf), obj, d, out->opts->float_fmt);
|
@@ -1226,7 +1220,7 @@ int oj_dump_float_printf(char *buf, size_t blen, VALUE obj, double d, const char
|
|
1226
1220
|
if (17 <= cnt && (0 == strcmp("0001", buf + cnt - 4) || 0 == strcmp("9999", buf + cnt - 4))) {
|
1227
1221
|
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
1228
1222
|
|
1229
|
-
strcpy(buf,
|
1223
|
+
strcpy(buf, RSTRING_PTR(rstr));
|
1230
1224
|
cnt = (int)RSTRING_LEN(rstr);
|
1231
1225
|
}
|
1232
1226
|
return cnt;
|
data/ext/oj/dump_compat.c
CHANGED
@@ -129,7 +129,7 @@ dump_to_json(VALUE obj, Out out) {
|
|
129
129
|
oj_trace("to_json", obj, __FILE__, __LINE__, 0, TraceRubyOut);
|
130
130
|
}
|
131
131
|
|
132
|
-
s =
|
132
|
+
s = RSTRING_PTR(rs);
|
133
133
|
len = (int)RSTRING_LEN(rs);
|
134
134
|
|
135
135
|
assure_size(out, len + 1);
|
@@ -635,7 +635,7 @@ dump_float(VALUE obj, int depth, Out out, bool as_ok) {
|
|
635
635
|
} else {
|
636
636
|
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
637
637
|
|
638
|
-
strcpy(buf,
|
638
|
+
strcpy(buf, RSTRING_PTR(rstr));
|
639
639
|
cnt = (int)RSTRING_LEN(rstr);
|
640
640
|
}
|
641
641
|
assure_size(out, cnt);
|
@@ -886,7 +886,7 @@ dump_bignum(VALUE obj, int depth, Out out, bool as_ok) {
|
|
886
886
|
} else {
|
887
887
|
assure_size(out, cnt);
|
888
888
|
}
|
889
|
-
memcpy(out->cur,
|
889
|
+
memcpy(out->cur, RSTRING_PTR(rs), cnt);
|
890
890
|
out->cur += cnt;
|
891
891
|
if (dump_as_string) {
|
892
892
|
*out->cur++ = '"';
|
data/ext/oj/dump_object.c
CHANGED
@@ -36,7 +36,7 @@ static void dump_data(VALUE obj, int depth, Out out, bool as_ok) {
|
|
36
36
|
} else {
|
37
37
|
if (oj_bigdecimal_class == clas) {
|
38
38
|
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
39
|
-
const char * str =
|
39
|
+
const char * str = RSTRING_PTR(rstr);
|
40
40
|
int len = (int)RSTRING_LEN(rstr);
|
41
41
|
|
42
42
|
if (No != out->opts->bigdec_as_num) {
|
@@ -65,7 +65,7 @@ static void dump_obj(VALUE obj, int depth, Out out, bool as_ok) {
|
|
65
65
|
|
66
66
|
if (oj_bigdecimal_class == clas) {
|
67
67
|
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
68
|
-
const char * str =
|
68
|
+
const char * str = RSTRING_PTR(rstr);
|
69
69
|
int len = (int)RSTRING_LEN(rstr);
|
70
70
|
|
71
71
|
if (0 == strcasecmp("Infinity", str)) {
|
@@ -195,7 +195,7 @@ static void dump_str_class(VALUE obj, VALUE clas, int depth, Out out) {
|
|
195
195
|
if (Qundef != clas && rb_cString != clas) {
|
196
196
|
dump_obj_attrs(obj, clas, 0, depth, out);
|
197
197
|
} else {
|
198
|
-
const char *s =
|
198
|
+
const char *s = RSTRING_PTR(obj);
|
199
199
|
size_t len = (int)RSTRING_LEN(obj);
|
200
200
|
char s1 = s[1];
|
201
201
|
|
@@ -208,9 +208,9 @@ static void dump_str(VALUE obj, int depth, Out out, bool as_ok) {
|
|
208
208
|
}
|
209
209
|
|
210
210
|
static void dump_sym(VALUE obj, int depth, Out out, bool as_ok) {
|
211
|
-
volatile VALUE s =
|
211
|
+
volatile VALUE s = rb_sym2str(obj);
|
212
212
|
|
213
|
-
oj_dump_cstr(
|
213
|
+
oj_dump_cstr(RSTRING_PTR(s), (int)RSTRING_LEN(s), 1, 0, out);
|
214
214
|
}
|
215
215
|
|
216
216
|
static int hash_cb(VALUE key, VALUE value, VALUE ov) {
|
@@ -414,7 +414,7 @@ static void dump_odd(VALUE obj, Odd odd, VALUE clas, int depth, Out out) {
|
|
414
414
|
if (Qundef == v || T_STRING != rb_type(v)) {
|
415
415
|
rb_raise(rb_eEncodingError, "Invalid type for raw JSON.");
|
416
416
|
} else {
|
417
|
-
const char *s =
|
417
|
+
const char *s = RSTRING_PTR(v);
|
418
418
|
int len = (int)RSTRING_LEN(v);
|
419
419
|
const char *name = rb_id2name(*odd->attrs);
|
420
420
|
size_t nlen = strlen(name);
|
@@ -532,7 +532,7 @@ static void dump_obj_attrs(VALUE obj, VALUE clas, slot_t id, int depth, Out out)
|
|
532
532
|
*out->cur++ = 'f';
|
533
533
|
*out->cur++ = '"';
|
534
534
|
*out->cur++ = ':';
|
535
|
-
oj_dump_cstr(
|
535
|
+
oj_dump_cstr(RSTRING_PTR(obj), (int)RSTRING_LEN(obj), 0, 0, out);
|
536
536
|
break;
|
537
537
|
case T_ARRAY:
|
538
538
|
assure_size(out, d2 * out->indent + 14);
|
@@ -694,9 +694,9 @@ static void dump_struct(VALUE obj, int depth, Out out, bool as_ok) {
|
|
694
694
|
|
695
695
|
*out->cur++ = '[';
|
696
696
|
for (i = 0; i < cnt; i++) {
|
697
|
-
volatile VALUE s =
|
697
|
+
volatile VALUE s = rb_sym2str(rb_ary_entry(ma, i));
|
698
698
|
|
699
|
-
name =
|
699
|
+
name = RSTRING_PTR(s);
|
700
700
|
len = (int)RSTRING_LEN(s);
|
701
701
|
size = len + 3;
|
702
702
|
assure_size(out, size);
|
data/ext/oj/dump_strict.c
CHANGED
@@ -98,7 +98,7 @@ static void dump_float(VALUE obj, int depth, Out out, bool as_ok) {
|
|
98
98
|
if ((int)sizeof(buf) <= cnt) {
|
99
99
|
cnt = sizeof(buf) - 1;
|
100
100
|
}
|
101
|
-
|
101
|
+
memcpy(buf, RSTRING_PTR(rstr), cnt);
|
102
102
|
buf[cnt] = '\0';
|
103
103
|
} else {
|
104
104
|
cnt = oj_dump_float_printf(buf, sizeof(buf), obj, d, out->opts->float_fmt);
|
@@ -304,7 +304,7 @@ static void dump_data_strict(VALUE obj, int depth, Out out, bool as_ok) {
|
|
304
304
|
if (oj_bigdecimal_class == clas) {
|
305
305
|
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
306
306
|
|
307
|
-
oj_dump_raw(
|
307
|
+
oj_dump_raw(RSTRING_PTR(rstr), (int)RSTRING_LEN(rstr), out);
|
308
308
|
} else {
|
309
309
|
raise_strict(obj);
|
310
310
|
}
|
@@ -316,7 +316,7 @@ static void dump_data_null(VALUE obj, int depth, Out out, bool as_ok) {
|
|
316
316
|
if (oj_bigdecimal_class == clas) {
|
317
317
|
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
318
318
|
|
319
|
-
oj_dump_raw(
|
319
|
+
oj_dump_raw(RSTRING_PTR(rstr), (int)RSTRING_LEN(rstr), out);
|
320
320
|
} else {
|
321
321
|
oj_dump_nil(Qnil, depth, out, false);
|
322
322
|
}
|
data/ext/oj/err.h
CHANGED
@@ -4,12 +4,31 @@
|
|
4
4
|
#ifndef OJ_ERR_H
|
5
5
|
#define OJ_ERR_H
|
6
6
|
|
7
|
+
#include <errno.h>
|
7
8
|
#include "ruby.h"
|
9
|
+
|
8
10
|
// Needed to silence 2.4.0 warnings.
|
9
11
|
#ifndef NORETURN
|
10
12
|
#define NORETURN(x) x
|
11
13
|
#endif
|
12
14
|
|
15
|
+
#define OJ_ERR_START 300
|
16
|
+
|
17
|
+
typedef enum {
|
18
|
+
OJ_OK = 0,
|
19
|
+
OJ_ERR_MEMORY = ENOMEM,
|
20
|
+
OJ_ERR_PARSE = OJ_ERR_START,
|
21
|
+
OJ_ERR_READ,
|
22
|
+
OJ_ERR_WRITE,
|
23
|
+
OJ_ERR_OVERFLOW,
|
24
|
+
OJ_ERR_ARG,
|
25
|
+
OJ_ERR_TOO_MANY,
|
26
|
+
OJ_ERR_TYPE,
|
27
|
+
OJ_ERR_KEY,
|
28
|
+
OJ_ABORT,
|
29
|
+
OJ_ERR_LAST,
|
30
|
+
} ojStatus;
|
31
|
+
|
13
32
|
#define set_error(err, eclas, msg, json, current) \
|
14
33
|
_oj_err_set_with_location(err, eclas, msg, json, current, FILE, LINE)
|
15
34
|
|
data/ext/oj/extconf.rb
CHANGED
@@ -30,6 +30,10 @@ have_func('rb_ivar_foreach')
|
|
30
30
|
have_func('rb_gc_mark_movable')
|
31
31
|
have_func('stpcpy')
|
32
32
|
have_func('pthread_mutex_init')
|
33
|
+
have_func('rb_enc_associate')
|
34
|
+
have_func('rb_ext_ractor_safe', 'ruby.h')
|
35
|
+
# rb_hash_bulk_insert is deep down in a header not included in normal build and that seems to fool have_func.
|
36
|
+
have_func('rb_hash_bulk_insert', 'ruby.h') unless '2' == version[0] && '6' == version[1]
|
33
37
|
|
34
38
|
dflags['OJ_DEBUG'] = true unless ENV['OJ_DEBUG'].nil?
|
35
39
|
|
data/ext/oj/fast.c
CHANGED
@@ -200,9 +200,7 @@ static VALUE leaf_value(Doc doc, Leaf leaf) {
|
|
200
200
|
break;
|
201
201
|
case T_ARRAY: return leaf_array_value(doc, leaf); break;
|
202
202
|
case T_HASH: return leaf_hash_value(doc, leaf); break;
|
203
|
-
default:
|
204
|
-
rb_raise(rb_const_get_at(Oj, rb_intern("Error")), "Unexpected type %02x.", leaf->rtype);
|
205
|
-
break;
|
203
|
+
default: rb_raise(rb_const_get_at(Oj, rb_intern("Error")), "Unexpected type %02x.", leaf->rtype); break;
|
206
204
|
}
|
207
205
|
}
|
208
206
|
return leaf->value;
|
@@ -773,7 +771,7 @@ static VALUE parse_json(VALUE clas, char *json, bool given, bool allocated) {
|
|
773
771
|
pi.doc = doc;
|
774
772
|
#if IS_WINDOWS
|
775
773
|
// assume a 1M stack and give half to ruby
|
776
|
-
pi.stack_min = (void
|
774
|
+
pi.stack_min = (void*)((char*)&pi - (512 * 1024));
|
777
775
|
#else
|
778
776
|
{
|
779
777
|
struct rlimit lim;
|
@@ -825,9 +823,7 @@ static Leaf get_doc_leaf(Doc doc, const char *path) {
|
|
825
823
|
size_t cnt = doc->where - doc->where_path;
|
826
824
|
|
827
825
|
if (MAX_STACK <= cnt) {
|
828
|
-
rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")),
|
829
|
-
"Path too deep. Limit is %d levels.",
|
830
|
-
MAX_STACK);
|
826
|
+
rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
|
831
827
|
}
|
832
828
|
memcpy(stack, doc->where_path, sizeof(Leaf) * (cnt + 1));
|
833
829
|
lp = stack + cnt;
|
@@ -868,9 +864,7 @@ static Leaf get_leaf(Leaf *stack, Leaf *lp, const char *path) {
|
|
868
864
|
Leaf leaf = *lp;
|
869
865
|
|
870
866
|
if (MAX_STACK <= lp - stack) {
|
871
|
-
rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")),
|
872
|
-
"Path too deep. Limit is %d levels.",
|
873
|
-
MAX_STACK);
|
867
|
+
rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
|
874
868
|
}
|
875
869
|
if ('\0' != *path) {
|
876
870
|
if ('.' == *path && '.' == *(path + 1)) {
|
@@ -946,9 +940,7 @@ static void each_leaf(Doc doc, VALUE self) {
|
|
946
940
|
|
947
941
|
doc->where++;
|
948
942
|
if (MAX_STACK <= doc->where - doc->where_path) {
|
949
|
-
rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")),
|
950
|
-
"Path too deep. Limit is %d levels.",
|
951
|
-
MAX_STACK);
|
943
|
+
rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
|
952
944
|
}
|
953
945
|
do {
|
954
946
|
*doc->where = e;
|
@@ -964,9 +956,7 @@ static void each_leaf(Doc doc, VALUE self) {
|
|
964
956
|
|
965
957
|
static int move_step(Doc doc, const char *path, int loc) {
|
966
958
|
if (MAX_STACK <= doc->where - doc->where_path) {
|
967
|
-
rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")),
|
968
|
-
"Path too deep. Limit is %d levels.",
|
969
|
-
MAX_STACK);
|
959
|
+
rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
|
970
960
|
}
|
971
961
|
if ('\0' == *path) {
|
972
962
|
loc = 0;
|
@@ -1264,7 +1254,6 @@ static VALUE doc_path(VALUE self) {
|
|
1264
1254
|
return doc_where(self);
|
1265
1255
|
}
|
1266
1256
|
|
1267
|
-
|
1268
1257
|
/* @overload local_key() => String, Fixnum, nil
|
1269
1258
|
*
|
1270
1259
|
* Returns the final key to the current location.
|
data/ext/oj/intern.c
ADDED
@@ -0,0 +1,398 @@
|
|
1
|
+
// Copyright (c) 2011, 2021 Peter Ohler. All rights reserved.
|
2
|
+
// Licensed under the MIT License. See LICENSE file in the project root for license details.
|
3
|
+
|
4
|
+
#include "intern.h"
|
5
|
+
|
6
|
+
#include <stdint.h>
|
7
|
+
|
8
|
+
#if HAVE_PTHREAD_MUTEX_INIT
|
9
|
+
#include <pthread.h>
|
10
|
+
#endif
|
11
|
+
#include "parse.h"
|
12
|
+
|
13
|
+
#define HASH_SLOT_CNT ((uint32_t)8192)
|
14
|
+
#define HASH_MASK (HASH_SLOT_CNT - 1)
|
15
|
+
|
16
|
+
typedef struct _keyVal {
|
17
|
+
struct _keyVal *next;
|
18
|
+
const char * key;
|
19
|
+
size_t len;
|
20
|
+
VALUE val;
|
21
|
+
} * KeyVal;
|
22
|
+
|
23
|
+
typedef struct _hash {
|
24
|
+
struct _keyVal slots[HASH_SLOT_CNT];
|
25
|
+
#if HAVE_PTHREAD_MUTEX_INIT
|
26
|
+
pthread_mutex_t mutex;
|
27
|
+
#else
|
28
|
+
VALUE mutex;
|
29
|
+
#endif
|
30
|
+
} * Hash;
|
31
|
+
|
32
|
+
struct _hash class_hash;
|
33
|
+
struct _hash str_hash;
|
34
|
+
struct _hash sym_hash;
|
35
|
+
struct _hash attr_hash;
|
36
|
+
|
37
|
+
// almost the Murmur hash algorithm
|
38
|
+
#define M 0x5bd1e995
|
39
|
+
#define C1 0xCC9E2D51
|
40
|
+
#define C2 0x1B873593
|
41
|
+
#define N 0xE6546B64
|
42
|
+
|
43
|
+
static uint32_t hash_calc(const uint8_t *key, size_t len) {
|
44
|
+
const uint8_t *end = key + len;
|
45
|
+
const uint8_t *endless = key + (len & 0xFFFFFFFC);
|
46
|
+
uint32_t h = (uint32_t)len;
|
47
|
+
uint32_t k;
|
48
|
+
|
49
|
+
while (key < endless) {
|
50
|
+
k = (uint32_t)*key++;
|
51
|
+
k |= (uint32_t)*key++ << 8;
|
52
|
+
k |= (uint32_t)*key++ << 16;
|
53
|
+
k |= (uint32_t)*key++ << 24;
|
54
|
+
|
55
|
+
k *= M;
|
56
|
+
k ^= k >> 24;
|
57
|
+
h *= M;
|
58
|
+
h ^= k * M;
|
59
|
+
}
|
60
|
+
if (1 < end - key) {
|
61
|
+
uint16_t k16 = (uint16_t)*key++;
|
62
|
+
|
63
|
+
k16 |= (uint16_t)*key++ << 8;
|
64
|
+
h ^= k16 << 8;
|
65
|
+
}
|
66
|
+
if (key < end) {
|
67
|
+
h ^= *key;
|
68
|
+
}
|
69
|
+
h *= M;
|
70
|
+
h ^= h >> 13;
|
71
|
+
h *= M;
|
72
|
+
h ^= h >> 15;
|
73
|
+
|
74
|
+
return h;
|
75
|
+
}
|
76
|
+
|
77
|
+
void oj_hash_init() {
|
78
|
+
memset(class_hash.slots, 0, sizeof(class_hash.slots));
|
79
|
+
memset(str_hash.slots, 0, sizeof(str_hash.slots));
|
80
|
+
memset(sym_hash.slots, 0, sizeof(sym_hash.slots));
|
81
|
+
memset(attr_hash.slots, 0, sizeof(attr_hash.slots));
|
82
|
+
#if HAVE_PTHREAD_MUTEX_INIT
|
83
|
+
pthread_mutex_init(&class_hash.mutex, NULL);
|
84
|
+
pthread_mutex_init(&str_hash.mutex, NULL);
|
85
|
+
pthread_mutex_init(&sym_hash.mutex, NULL);
|
86
|
+
pthread_mutex_init(&attr_hash.mutex, NULL);
|
87
|
+
#else
|
88
|
+
class_hash.mutex = rb_mutex_new();
|
89
|
+
rb_gc_register_address(&class_hash.mutex);
|
90
|
+
str_hash.mutex = rb_mutex_new();
|
91
|
+
rb_gc_register_address(&str_hash.mutex);
|
92
|
+
sym_hash.mutex = rb_mutex_new();
|
93
|
+
rb_gc_register_address(&sym_hash.mutex);
|
94
|
+
attr_hash.mutex = rb_mutex_new();
|
95
|
+
rb_gc_register_address(&attr_hash.mutex);
|
96
|
+
#endif
|
97
|
+
}
|
98
|
+
|
99
|
+
void oj_hash_print() {
|
100
|
+
uint32_t i;
|
101
|
+
KeyVal b;
|
102
|
+
|
103
|
+
for (i = 0; i < HASH_SLOT_CNT; i++) {
|
104
|
+
printf("%4d:", i);
|
105
|
+
for (b = class_hash.slots + i; 0 != b && 0 != b->key; b = b->next) {
|
106
|
+
printf(" %s", b->key);
|
107
|
+
}
|
108
|
+
printf("\n");
|
109
|
+
}
|
110
|
+
}
|
111
|
+
|
112
|
+
void oj_hash_sizes() {
|
113
|
+
uint32_t i;
|
114
|
+
KeyVal b;
|
115
|
+
int max = 0;
|
116
|
+
int min = 1000000;
|
117
|
+
|
118
|
+
for (i = 0; i < HASH_SLOT_CNT; i++) {
|
119
|
+
int cnt = 0;
|
120
|
+
|
121
|
+
for (b = str_hash.slots + i; 0 != b && 0 != b->key; b = b->next) {
|
122
|
+
cnt++;
|
123
|
+
}
|
124
|
+
// printf(" %4d\n", cnt);
|
125
|
+
if (max < cnt) {
|
126
|
+
max = cnt;
|
127
|
+
}
|
128
|
+
if (cnt < min) {
|
129
|
+
min = cnt;
|
130
|
+
}
|
131
|
+
}
|
132
|
+
printf("min: %d max: %d\n", min, max);
|
133
|
+
}
|
134
|
+
|
135
|
+
VALUE
|
136
|
+
oj_str_intern(const char *key, size_t len) {
|
137
|
+
uint32_t h = hash_calc((const uint8_t *)key, len) & HASH_MASK;
|
138
|
+
KeyVal bucket = str_hash.slots + h;
|
139
|
+
KeyVal b;
|
140
|
+
|
141
|
+
#if HAVE_PTHREAD_MUTEX_INIT
|
142
|
+
pthread_mutex_lock(&str_hash.mutex);
|
143
|
+
#else
|
144
|
+
rb_mutex_lock(str_hash.mutex);
|
145
|
+
#endif
|
146
|
+
if (NULL != bucket->key) { // not the top slot
|
147
|
+
for (b = bucket; 0 != b; b = b->next) {
|
148
|
+
if (len == b->len && 0 == strncmp(b->key, key, len)) {
|
149
|
+
#if HAVE_PTHREAD_MUTEX_INIT
|
150
|
+
pthread_mutex_unlock(&str_hash.mutex);
|
151
|
+
#else
|
152
|
+
rb_mutex_unlock(str_hash.mutex);
|
153
|
+
#endif
|
154
|
+
return b->val;
|
155
|
+
}
|
156
|
+
bucket = b;
|
157
|
+
}
|
158
|
+
b = ALLOC(struct _keyVal);
|
159
|
+
b->next = NULL;
|
160
|
+
bucket->next = b;
|
161
|
+
bucket = b;
|
162
|
+
}
|
163
|
+
bucket->key = oj_strndup(key, len);
|
164
|
+
bucket->len = len;
|
165
|
+
bucket->val = rb_utf8_str_new(key, len);
|
166
|
+
bucket->val = rb_str_freeze(bucket->val);
|
167
|
+
rb_gc_register_address(&bucket->val);
|
168
|
+
#if HAVE_PTHREAD_MUTEX_INIT
|
169
|
+
pthread_mutex_unlock(&str_hash.mutex);
|
170
|
+
#else
|
171
|
+
rb_mutex_unlock(str_hash.mutex);
|
172
|
+
#endif
|
173
|
+
return bucket->val;
|
174
|
+
}
|
175
|
+
|
176
|
+
VALUE
|
177
|
+
oj_sym_intern(const char *key, size_t len) {
|
178
|
+
uint32_t h = hash_calc((const uint8_t *)key, len) & HASH_MASK;
|
179
|
+
KeyVal bucket = sym_hash.slots + h;
|
180
|
+
KeyVal b;
|
181
|
+
|
182
|
+
#if HAVE_PTHREAD_MUTEX_INIT
|
183
|
+
pthread_mutex_lock(&sym_hash.mutex);
|
184
|
+
#else
|
185
|
+
rb_mutex_lock(sym_hash.mutex);
|
186
|
+
#endif
|
187
|
+
if (NULL != bucket->key) { // not the top slot
|
188
|
+
for (b = bucket; 0 != b; b = b->next) {
|
189
|
+
if (len == b->len && 0 == strncmp(b->key, key, len)) {
|
190
|
+
#if HAVE_PTHREAD_MUTEX_INIT
|
191
|
+
pthread_mutex_unlock(&sym_hash.mutex);
|
192
|
+
#else
|
193
|
+
rb_mutex_unlock(sym_hash.mutex);
|
194
|
+
#endif
|
195
|
+
return b->val;
|
196
|
+
}
|
197
|
+
bucket = b;
|
198
|
+
}
|
199
|
+
b = ALLOC(struct _keyVal);
|
200
|
+
b->next = NULL;
|
201
|
+
bucket->next = b;
|
202
|
+
bucket = b;
|
203
|
+
}
|
204
|
+
bucket->key = oj_strndup(key, len);
|
205
|
+
bucket->len = len;
|
206
|
+
bucket->val = ID2SYM(rb_intern3(key, len, oj_utf8_encoding));
|
207
|
+
rb_gc_register_address(&bucket->val);
|
208
|
+
#if HAVE_PTHREAD_MUTEX_INIT
|
209
|
+
pthread_mutex_unlock(&sym_hash.mutex);
|
210
|
+
#else
|
211
|
+
rb_mutex_unlock(sym_hash.mutex);
|
212
|
+
#endif
|
213
|
+
return bucket->val;
|
214
|
+
}
|
215
|
+
|
216
|
+
static ID form_attr(const char *key, size_t klen) {
|
217
|
+
char attr[256];
|
218
|
+
ID var_id;
|
219
|
+
|
220
|
+
if ((int)sizeof(attr) <= klen + 2) {
|
221
|
+
char *buf = ALLOC_N(char, klen + 2);
|
222
|
+
|
223
|
+
if ('~' == *key) {
|
224
|
+
memcpy(buf, key + 1, klen - 1);
|
225
|
+
buf[klen - 1] = '\0';
|
226
|
+
} else {
|
227
|
+
*buf = '@';
|
228
|
+
memcpy(buf + 1, key, klen);
|
229
|
+
buf[klen + 1] = '\0';
|
230
|
+
}
|
231
|
+
var_id = rb_intern(buf);
|
232
|
+
xfree(buf);
|
233
|
+
} else {
|
234
|
+
if ('~' == *key) {
|
235
|
+
memcpy(attr, key + 1, klen - 1);
|
236
|
+
attr[klen - 1] = '\0';
|
237
|
+
} else {
|
238
|
+
*attr = '@';
|
239
|
+
memcpy(attr + 1, key, klen);
|
240
|
+
attr[klen + 1] = '\0';
|
241
|
+
}
|
242
|
+
var_id = rb_intern(attr);
|
243
|
+
}
|
244
|
+
return var_id;
|
245
|
+
}
|
246
|
+
|
247
|
+
ID oj_attr_intern(const char *key, size_t len) {
|
248
|
+
uint32_t h = hash_calc((const uint8_t *)key, len) & HASH_MASK;
|
249
|
+
KeyVal bucket = attr_hash.slots + h;
|
250
|
+
KeyVal b;
|
251
|
+
|
252
|
+
#if HAVE_PTHREAD_MUTEX_INIT
|
253
|
+
pthread_mutex_lock(&attr_hash.mutex);
|
254
|
+
#else
|
255
|
+
rb_mutex_lock(attr_hash.mutex);
|
256
|
+
#endif
|
257
|
+
if (NULL != bucket->key) { // not the top slot
|
258
|
+
for (b = bucket; 0 != b; b = b->next) {
|
259
|
+
if (len == b->len && 0 == strncmp(b->key, key, len)) {
|
260
|
+
#if HAVE_PTHREAD_MUTEX_INIT
|
261
|
+
pthread_mutex_unlock(&attr_hash.mutex);
|
262
|
+
#else
|
263
|
+
rb_mutex_unlock(attr_hash.mutex);
|
264
|
+
#endif
|
265
|
+
return (ID)b->val;
|
266
|
+
}
|
267
|
+
bucket = b;
|
268
|
+
}
|
269
|
+
b = ALLOC(struct _keyVal);
|
270
|
+
b->next = NULL;
|
271
|
+
bucket->next = b;
|
272
|
+
bucket = b;
|
273
|
+
}
|
274
|
+
bucket->key = oj_strndup(key, len);
|
275
|
+
bucket->len = len;
|
276
|
+
bucket->val = (VALUE)form_attr(key, len);
|
277
|
+
#if HAVE_PTHREAD_MUTEX_INIT
|
278
|
+
pthread_mutex_unlock(&attr_hash.mutex);
|
279
|
+
#else
|
280
|
+
rb_mutex_unlock(attr_hash.mutex);
|
281
|
+
#endif
|
282
|
+
return (ID)bucket->val;
|
283
|
+
}
|
284
|
+
|
285
|
+
static VALUE resolve_classname(VALUE mod, const char *classname, int auto_define) {
|
286
|
+
VALUE clas;
|
287
|
+
ID ci = rb_intern(classname);
|
288
|
+
|
289
|
+
if (rb_const_defined_at(mod, ci)) {
|
290
|
+
clas = rb_const_get_at(mod, ci);
|
291
|
+
} else if (auto_define) {
|
292
|
+
clas = rb_define_class_under(mod, classname, oj_bag_class);
|
293
|
+
} else {
|
294
|
+
clas = Qundef;
|
295
|
+
}
|
296
|
+
return clas;
|
297
|
+
}
|
298
|
+
|
299
|
+
static VALUE resolve_classpath(ParseInfo pi, const char *name, size_t len, int auto_define, VALUE error_class) {
|
300
|
+
char class_name[1024];
|
301
|
+
VALUE clas;
|
302
|
+
char * end = class_name + sizeof(class_name) - 1;
|
303
|
+
char * s;
|
304
|
+
const char *n = name;
|
305
|
+
|
306
|
+
clas = rb_cObject;
|
307
|
+
for (s = class_name; 0 < len; n++, len--) {
|
308
|
+
if (':' == *n) {
|
309
|
+
*s = '\0';
|
310
|
+
n++;
|
311
|
+
len--;
|
312
|
+
if (':' != *n) {
|
313
|
+
return Qundef;
|
314
|
+
}
|
315
|
+
if (Qundef == (clas = resolve_classname(clas, class_name, auto_define))) {
|
316
|
+
return Qundef;
|
317
|
+
}
|
318
|
+
s = class_name;
|
319
|
+
} else if (end <= s) {
|
320
|
+
return Qundef;
|
321
|
+
} else {
|
322
|
+
*s++ = *n;
|
323
|
+
}
|
324
|
+
}
|
325
|
+
*s = '\0';
|
326
|
+
if (Qundef == (clas = resolve_classname(clas, class_name, auto_define))) {
|
327
|
+
oj_set_error_at(pi, error_class, __FILE__, __LINE__, "class %s is not defined", name);
|
328
|
+
if (Qnil != error_class) {
|
329
|
+
pi->err_class = error_class;
|
330
|
+
}
|
331
|
+
}
|
332
|
+
return clas;
|
333
|
+
}
|
334
|
+
|
335
|
+
VALUE oj_class_intern(const char *key, size_t len, bool safe, ParseInfo pi, int auto_define, VALUE error_class) {
|
336
|
+
uint32_t h = hash_calc((const uint8_t *)key, len) & HASH_MASK;
|
337
|
+
KeyVal bucket = class_hash.slots + h;
|
338
|
+
KeyVal b;
|
339
|
+
|
340
|
+
if (safe) {
|
341
|
+
#if HAVE_PTHREAD_MUTEX_INIT
|
342
|
+
pthread_mutex_lock(&class_hash.mutex);
|
343
|
+
#else
|
344
|
+
rb_mutex_lock(class_hash.mutex);
|
345
|
+
#endif
|
346
|
+
if (NULL != bucket->key) { // not the top slot
|
347
|
+
for (b = bucket; 0 != b; b = b->next) {
|
348
|
+
if (len == b->len && 0 == strncmp(b->key, key, len)) {
|
349
|
+
#if HAVE_PTHREAD_MUTEX_INIT
|
350
|
+
pthread_mutex_unlock(&class_hash.mutex);
|
351
|
+
#else
|
352
|
+
rb_mutex_unlock(class_hash.mutex);
|
353
|
+
#endif
|
354
|
+
return b->val;
|
355
|
+
}
|
356
|
+
bucket = b;
|
357
|
+
}
|
358
|
+
b = ALLOC(struct _keyVal);
|
359
|
+
b->next = NULL;
|
360
|
+
bucket->next = b;
|
361
|
+
bucket = b;
|
362
|
+
}
|
363
|
+
bucket->key = oj_strndup(key, len);
|
364
|
+
bucket->len = len;
|
365
|
+
bucket->val = resolve_classpath(pi, key, len, auto_define, error_class);
|
366
|
+
#if HAVE_PTHREAD_MUTEX_INIT
|
367
|
+
pthread_mutex_unlock(&class_hash.mutex);
|
368
|
+
#else
|
369
|
+
rb_mutex_unlock(class_hash.mutex);
|
370
|
+
#endif
|
371
|
+
} else {
|
372
|
+
if (NULL != bucket->key) {
|
373
|
+
for (b = bucket; 0 != b; b = b->next) {
|
374
|
+
if (len == b->len && 0 == strncmp(b->key, key, len)) {
|
375
|
+
return (ID)b->val;
|
376
|
+
}
|
377
|
+
bucket = b;
|
378
|
+
}
|
379
|
+
b = ALLOC(struct _keyVal);
|
380
|
+
b->next = NULL;
|
381
|
+
bucket->next = b;
|
382
|
+
bucket = b;
|
383
|
+
}
|
384
|
+
bucket->key = oj_strndup(key, len);
|
385
|
+
bucket->len = len;
|
386
|
+
bucket->val = resolve_classpath(pi, key, len, auto_define, error_class);
|
387
|
+
}
|
388
|
+
return bucket->val;
|
389
|
+
}
|
390
|
+
|
391
|
+
char *oj_strndup(const char *s, size_t len) {
|
392
|
+
char *d = ALLOC_N(char, len + 1);
|
393
|
+
|
394
|
+
memcpy(d, s, len);
|
395
|
+
d[len] = '\0';
|
396
|
+
|
397
|
+
return d;
|
398
|
+
}
|