oj 3.11.5 → 3.12.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 +5 -1
- data/ext/oj/compat.c +32 -27
- data/ext/oj/custom.c +3 -15
- data/ext/oj/fast.c +53 -22
- data/ext/oj/hash.c +41 -4
- data/ext/oj/hash.h +2 -0
- data/ext/oj/mimic_json.c +6 -4
- data/ext/oj/object.c +29 -1
- data/ext/oj/oj.c +51 -3
- data/ext/oj/oj.h +2 -0
- data/ext/oj/parse.c +1 -1
- data/ext/oj/parse.h +3 -0
- data/ext/oj/rails.c +2 -1
- data/ext/oj/scp.c +4 -16
- data/ext/oj/strict.c +67 -22
- data/ext/oj/wab.c +35 -18
- data/lib/oj/mimic.rb +2 -0
- data/lib/oj/version.rb +1 -1
- data/test/foo.rb +8 -43
- data/test/perf.rb +1 -1
- data/test/perf_scp.rb +11 -10
- data/test/perf_strict.rb +17 -23
- data/test/test_fast.rb +32 -2
- data/test/test_various.rb +2 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7316e25014cf4268799f3f1e4a01a74fada832b1d68bf64d82cbeb42b2caf1dd
|
4
|
+
data.tar.gz: a02fdb9676efeacfec86475ff35afdb88cfef33e0c94188355cad69401ee2304
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8f83d20545998db46a7dd0bd0e2560f47198fbb0f192bc80f17ffbce046e38727e0c143e022f8d00ba2332e2cb0be27ddda294b6a96d6f4b091ec95e282d2f01
|
7
|
+
data.tar.gz: e62a125cf992f510296bbe6b3eeeaaadaa8ccd2891b764a290518f785b706a797af7fbd247ac360364cd610e67ad038488d6eefb55e4dc028a3b20540995e4df
|
data/README.md
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
# [](http://www.ohler.com/oj) gem
|
2
2
|
|
3
|
-
[](https://github.com/ohler55/oj/actions/workflows/CI.yml)
|
4
|
+

|
5
|
+

|
6
|
+
[](https://dependabot.com/compatibility-score.html?dependency-name=oj&package-manager=bundler&version-scheme=semver)
|
7
|
+
[](https://tidelift.com/subscription/pkg/rubygems-oj?utm_source=rubygems-oj&utm_medium=referral&utm_campaign=readme)
|
4
8
|
|
5
9
|
A *fast* JSON parser and Object marshaller as a Ruby gem.
|
6
10
|
|
data/ext/oj/compat.c
CHANGED
@@ -23,14 +23,34 @@ static void hash_set_cstr(ParseInfo pi, Val kval, const char *str, size_t len, c
|
|
23
23
|
parent->classname = oj_strndup(str, len);
|
24
24
|
parent->clen = len;
|
25
25
|
} else {
|
26
|
-
volatile VALUE rstr =
|
26
|
+
volatile VALUE rstr = oj_cstr_to_value(str, len, (size_t)pi->options.cache_str);
|
27
27
|
|
28
28
|
if (Qundef == rkey) {
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
29
|
+
if (Yes != pi->options.cache_keys) {
|
30
|
+
rkey = rb_str_new(key, klen);
|
31
|
+
rkey = oj_encode(rkey);
|
32
|
+
if (Yes == pi->options.sym_key) {
|
33
|
+
rkey = rb_str_intern(rkey);
|
34
|
+
}
|
35
|
+
} else {
|
36
|
+
VALUE *slot;
|
37
|
+
|
38
|
+
if (Yes == pi->options.sym_key) {
|
39
|
+
if (Qnil == (rkey = oj_sym_hash_get(key, klen, &slot))) {
|
40
|
+
rkey = rb_str_new(key, klen);
|
41
|
+
rkey = oj_encode(rkey);
|
42
|
+
rkey = rb_str_intern(rkey);
|
43
|
+
*slot = rkey;
|
44
|
+
rb_gc_register_address(slot);
|
45
|
+
}
|
46
|
+
} else {
|
47
|
+
if (Qnil == (rkey = oj_str_hash_get(key, klen, &slot))) {
|
48
|
+
rkey = rb_str_new(key, klen);
|
49
|
+
rkey = oj_encode(rkey);
|
50
|
+
*slot = rkey;
|
51
|
+
rb_gc_register_address(slot);
|
52
|
+
}
|
53
|
+
}
|
34
54
|
}
|
35
55
|
}
|
36
56
|
if (Yes == pi->options.create_ok && NULL != pi->options.str_rx.head) {
|
@@ -93,23 +113,9 @@ static void end_hash(struct _parseInfo *pi) {
|
|
93
113
|
}
|
94
114
|
}
|
95
115
|
|
96
|
-
static VALUE calc_hash_key(ParseInfo pi, Val parent) {
|
97
|
-
volatile VALUE rkey = parent->key_val;
|
98
|
-
|
99
|
-
if (Qundef == rkey) {
|
100
|
-
rkey = rb_str_new(parent->key, parent->klen);
|
101
|
-
}
|
102
|
-
rkey = oj_encode(rkey);
|
103
|
-
if (Yes == pi->options.sym_key) {
|
104
|
-
rkey = rb_str_intern(rkey);
|
105
|
-
}
|
106
|
-
return rkey;
|
107
|
-
}
|
108
|
-
|
109
116
|
static void add_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
|
110
|
-
volatile VALUE rstr =
|
117
|
+
volatile VALUE rstr = oj_cstr_to_value(str, len, (size_t)pi->options.cache_str);
|
111
118
|
|
112
|
-
rstr = oj_encode(rstr);
|
113
119
|
if (Yes == pi->options.create_ok && NULL != pi->options.str_rx.head) {
|
114
120
|
VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, (int)len);
|
115
121
|
|
@@ -142,10 +148,10 @@ static void hash_set_num(struct _parseInfo *pi, Val parent, NumInfo ni) {
|
|
142
148
|
rb_funcall(stack_peek(&pi->stack)->val,
|
143
149
|
rb_intern("[]="),
|
144
150
|
2,
|
145
|
-
|
151
|
+
oj_calc_hash_key(pi, parent),
|
146
152
|
rval);
|
147
153
|
} else {
|
148
|
-
rb_hash_aset(stack_peek(&pi->stack)->val,
|
154
|
+
rb_hash_aset(stack_peek(&pi->stack)->val, oj_calc_hash_key(pi, parent), rval);
|
149
155
|
}
|
150
156
|
if (Yes == pi->options.trace) {
|
151
157
|
oj_trace_parse_call("set_number", pi, __FILE__, __LINE__, rval);
|
@@ -161,10 +167,10 @@ static void hash_set_value(ParseInfo pi, Val parent, VALUE value) {
|
|
161
167
|
rb_funcall(stack_peek(&pi->stack)->val,
|
162
168
|
rb_intern("[]="),
|
163
169
|
2,
|
164
|
-
|
170
|
+
oj_calc_hash_key(pi, parent),
|
165
171
|
value);
|
166
172
|
} else {
|
167
|
-
rb_hash_aset(stack_peek(&pi->stack)->val,
|
173
|
+
rb_hash_aset(stack_peek(&pi->stack)->val, oj_calc_hash_key(pi, parent), value);
|
168
174
|
}
|
169
175
|
if (Yes == pi->options.trace) {
|
170
176
|
oj_trace_parse_call("set_value", pi, __FILE__, __LINE__, value);
|
@@ -199,9 +205,8 @@ static void array_append_num(ParseInfo pi, NumInfo ni) {
|
|
199
205
|
}
|
200
206
|
|
201
207
|
static void array_append_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
|
202
|
-
volatile VALUE rstr =
|
208
|
+
volatile VALUE rstr = oj_cstr_to_value(str, len, (size_t)pi->options.cache_str);
|
203
209
|
|
204
|
-
rstr = oj_encode(rstr);
|
205
210
|
if (Yes == pi->options.create_ok && NULL != pi->options.str_rx.head) {
|
206
211
|
VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, (int)len);
|
207
212
|
|
data/ext/oj/custom.c
CHANGED
@@ -955,6 +955,7 @@ static void hash_set_cstr(ParseInfo pi, Val kval, const char *str, size_t len, c
|
|
955
955
|
}
|
956
956
|
}
|
957
957
|
} else {
|
958
|
+
//volatile VALUE rstr = oj_cstr_to_value(str, len, (size_t)pi->options.cache_str);
|
958
959
|
volatile VALUE rstr = rb_str_new(str, len);
|
959
960
|
|
960
961
|
if (Qundef == rkey) {
|
@@ -1010,19 +1011,6 @@ static void end_hash(struct _parseInfo *pi) {
|
|
1010
1011
|
}
|
1011
1012
|
}
|
1012
1013
|
|
1013
|
-
static VALUE calc_hash_key(ParseInfo pi, Val parent) {
|
1014
|
-
volatile VALUE rkey = parent->key_val;
|
1015
|
-
|
1016
|
-
if (Qundef == rkey) {
|
1017
|
-
rkey = rb_str_new(parent->key, parent->klen);
|
1018
|
-
}
|
1019
|
-
rkey = oj_encode(rkey);
|
1020
|
-
if (Yes == pi->options.sym_key) {
|
1021
|
-
rkey = rb_str_intern(rkey);
|
1022
|
-
}
|
1023
|
-
return rkey;
|
1024
|
-
}
|
1025
|
-
|
1026
1014
|
static void hash_set_num(struct _parseInfo *pi, Val kval, NumInfo ni) {
|
1027
1015
|
Val parent = stack_peek(&pi->stack);
|
1028
1016
|
volatile VALUE rval = oj_num_as_value(ni);
|
@@ -1067,7 +1055,7 @@ static void hash_set_num(struct _parseInfo *pi, Val kval, NumInfo ni) {
|
|
1067
1055
|
}
|
1068
1056
|
rval = parent->val;
|
1069
1057
|
} else {
|
1070
|
-
rb_hash_aset(parent->val,
|
1058
|
+
rb_hash_aset(parent->val, oj_calc_hash_key(pi, kval), rval);
|
1071
1059
|
}
|
1072
1060
|
break;
|
1073
1061
|
default: break;
|
@@ -1082,7 +1070,7 @@ static void hash_set_value(ParseInfo pi, Val kval, VALUE value) {
|
|
1082
1070
|
|
1083
1071
|
switch (rb_type(parent->val)) {
|
1084
1072
|
case T_OBJECT: oj_set_obj_ivar(parent, kval, value); break;
|
1085
|
-
case T_HASH: rb_hash_aset(parent->val,
|
1073
|
+
case T_HASH: rb_hash_aset(parent->val, oj_calc_hash_key(pi, kval), value); break;
|
1086
1074
|
default: break;
|
1087
1075
|
}
|
1088
1076
|
if (Yes == pi->options.trace) {
|
data/ext/oj/fast.c
CHANGED
@@ -80,22 +80,6 @@ static Leaf get_doc_leaf(Doc doc, const char *path);
|
|
80
80
|
static Leaf get_leaf(Leaf *stack, Leaf *lp, const char *path);
|
81
81
|
static void each_value(Doc doc, Leaf leaf);
|
82
82
|
|
83
|
-
static void doc_init(Doc doc);
|
84
|
-
static void doc_free(Doc doc);
|
85
|
-
static VALUE doc_open(VALUE clas, VALUE str);
|
86
|
-
static VALUE doc_open_file(VALUE clas, VALUE filename);
|
87
|
-
static VALUE doc_where(VALUE self);
|
88
|
-
static VALUE doc_local_key(VALUE self);
|
89
|
-
static VALUE doc_home(VALUE self);
|
90
|
-
static VALUE doc_type(int argc, VALUE *argv, VALUE self);
|
91
|
-
static VALUE doc_fetch(int argc, VALUE *argv, VALUE self);
|
92
|
-
static VALUE doc_each_leaf(int argc, VALUE *argv, VALUE self);
|
93
|
-
static VALUE doc_move(VALUE self, VALUE str);
|
94
|
-
static VALUE doc_each_child(int argc, VALUE *argv, VALUE self);
|
95
|
-
static VALUE doc_each_value(int argc, VALUE *argv, VALUE self);
|
96
|
-
static VALUE doc_dump(int argc, VALUE *argv, VALUE self);
|
97
|
-
static VALUE doc_size(VALUE self);
|
98
|
-
|
99
83
|
VALUE oj_doc_class = Qundef;
|
100
84
|
|
101
85
|
// This is only for CentOS 5.4 with Ruby 1.9.3-p0.
|
@@ -899,12 +883,14 @@ static Leaf get_leaf(Leaf *stack, Leaf *lp, const char *path) {
|
|
899
883
|
} else {
|
900
884
|
return 0;
|
901
885
|
}
|
902
|
-
} else if (
|
886
|
+
} else if (NULL == leaf->elements) {
|
887
|
+
leaf = NULL;
|
888
|
+
} else if (COL_VAL == leaf->value_type) {
|
903
889
|
Leaf first = leaf->elements->next;
|
904
890
|
Leaf e = first;
|
905
891
|
int type = leaf->rtype;
|
906
892
|
|
907
|
-
leaf =
|
893
|
+
leaf = NULL;
|
908
894
|
if (T_ARRAY == type) {
|
909
895
|
int cnt = 0;
|
910
896
|
|
@@ -929,6 +915,7 @@ static Leaf get_leaf(Leaf *stack, Leaf *lp, const char *path) {
|
|
929
915
|
const char *slash = next_slash(path);
|
930
916
|
int klen;
|
931
917
|
|
918
|
+
leaf = NULL;
|
932
919
|
if (0 == slash) {
|
933
920
|
klen = (int)strlen(key);
|
934
921
|
path += klen;
|
@@ -1217,7 +1204,7 @@ static char *append_key(char *p, const char *key) {
|
|
1217
1204
|
* @see Oj::Doc.open
|
1218
1205
|
*/
|
1219
1206
|
|
1220
|
-
/* @overload where
|
1207
|
+
/* @overload where() => String
|
1221
1208
|
*
|
1222
1209
|
* Returns a String that describes the absolute path to the current location
|
1223
1210
|
* in the JSON document.
|
@@ -1259,6 +1246,25 @@ static VALUE doc_where(VALUE self) {
|
|
1259
1246
|
}
|
1260
1247
|
}
|
1261
1248
|
|
1249
|
+
/* @overload where?() => String
|
1250
|
+
* @deprecated
|
1251
|
+
* Returns a String that describes the absolute path to the current location
|
1252
|
+
* in the JSON document.
|
1253
|
+
*/
|
1254
|
+
static VALUE doc_where_q(VALUE self) {
|
1255
|
+
return doc_where(self);
|
1256
|
+
}
|
1257
|
+
|
1258
|
+
/* @overload path() => String
|
1259
|
+
*
|
1260
|
+
* Returns a String that describes the absolute path to the current location
|
1261
|
+
* in the JSON document.
|
1262
|
+
*/
|
1263
|
+
static VALUE doc_path(VALUE self) {
|
1264
|
+
return doc_where(self);
|
1265
|
+
}
|
1266
|
+
|
1267
|
+
|
1262
1268
|
/* @overload local_key() => String, Fixnum, nil
|
1263
1269
|
*
|
1264
1270
|
* Returns the final key to the current location.
|
@@ -1340,14 +1346,14 @@ static VALUE doc_type(int argc, VALUE *argv, VALUE self) {
|
|
1340
1346
|
return type;
|
1341
1347
|
}
|
1342
1348
|
|
1343
|
-
/* @overload fetch(path=nil) => nil, true, false, Fixnum, Float, String, Array,
|
1349
|
+
/* @overload fetch(path=nil,default=nil) => nil, true, false, Fixnum, Float, String, Array,
|
1344
1350
|
* Hash
|
1345
1351
|
*
|
1346
1352
|
* Returns the value at the location identified by the path or the current
|
1347
1353
|
* location if the path is nil or not provided. This method will create and
|
1348
1354
|
* return an Array or Hash if that is the type of Object at the location
|
1349
1355
|
* specified. This is more expensive than navigating to the leaves of the JSON
|
1350
|
-
* document.
|
1356
|
+
* document. If a default is provided that is used if no value if found.
|
1351
1357
|
* @param [String] path path to the location to get the type of if provided
|
1352
1358
|
* @example
|
1353
1359
|
* Oj::Doc.open('[1,2]') { |doc| doc.fetch() } #=> [1, 2]
|
@@ -1373,6 +1379,28 @@ static VALUE doc_fetch(int argc, VALUE *argv, VALUE self) {
|
|
1373
1379
|
return val;
|
1374
1380
|
}
|
1375
1381
|
|
1382
|
+
/* @overload exists?(path) => true, false
|
1383
|
+
*
|
1384
|
+
* Returns true if the value at the location identified by the path exists.
|
1385
|
+
* @param [String] path path to the location
|
1386
|
+
* @example
|
1387
|
+
* Oj::Doc.open('[1,2]') { |doc| doc.exists('/1') } #=> true
|
1388
|
+
* Oj::Doc.open('[1,2]') { |doc| doc.exists('/3') } #=> false
|
1389
|
+
*/
|
1390
|
+
static VALUE doc_exists(VALUE self, VALUE str) {
|
1391
|
+
Doc doc;
|
1392
|
+
Leaf leaf;
|
1393
|
+
|
1394
|
+
doc = self_doc(self);
|
1395
|
+
Check_Type(str, T_STRING);
|
1396
|
+
if (0 != (leaf = get_doc_leaf(doc, StringValuePtr(str)))) {
|
1397
|
+
if (NULL != leaf) {
|
1398
|
+
return Qtrue;
|
1399
|
+
}
|
1400
|
+
}
|
1401
|
+
return Qfalse;
|
1402
|
+
}
|
1403
|
+
|
1376
1404
|
/* @overload each_leaf(path=nil) => nil
|
1377
1405
|
*
|
1378
1406
|
* Yields to the provided block for each leaf node with the identified
|
@@ -1695,11 +1723,14 @@ void oj_init_doc() {
|
|
1695
1723
|
rb_define_singleton_method(oj_doc_class, "open", doc_open, 1);
|
1696
1724
|
rb_define_singleton_method(oj_doc_class, "open_file", doc_open_file, 1);
|
1697
1725
|
rb_define_singleton_method(oj_doc_class, "parse", doc_open, 1);
|
1698
|
-
rb_define_method(oj_doc_class, "where?",
|
1726
|
+
rb_define_method(oj_doc_class, "where?", doc_where_q, 0);
|
1727
|
+
rb_define_method(oj_doc_class, "where", doc_where, 0);
|
1728
|
+
rb_define_method(oj_doc_class, "path", doc_path, 0);
|
1699
1729
|
rb_define_method(oj_doc_class, "local_key", doc_local_key, 0);
|
1700
1730
|
rb_define_method(oj_doc_class, "home", doc_home, 0);
|
1701
1731
|
rb_define_method(oj_doc_class, "type", doc_type, -1);
|
1702
1732
|
rb_define_method(oj_doc_class, "fetch", doc_fetch, -1);
|
1733
|
+
rb_define_method(oj_doc_class, "exists?", doc_exists, 1);
|
1703
1734
|
rb_define_method(oj_doc_class, "each_leaf", doc_each_leaf, -1);
|
1704
1735
|
rb_define_method(oj_doc_class, "move", doc_move, 1);
|
1705
1736
|
rb_define_method(oj_doc_class, "each_child", doc_each_child, -1);
|
data/ext/oj/hash.c
CHANGED
@@ -5,8 +5,8 @@
|
|
5
5
|
|
6
6
|
#include <stdint.h>
|
7
7
|
|
8
|
-
#define
|
9
|
-
#define HASH_SLOT_CNT
|
8
|
+
#define HASH_SLOT_CNT ((uint32_t)8192)
|
9
|
+
#define HASH_MASK (HASH_SLOT_CNT - 1)
|
10
10
|
|
11
11
|
typedef struct _keyVal {
|
12
12
|
struct _keyVal *next;
|
@@ -20,6 +20,8 @@ struct _hash {
|
|
20
20
|
};
|
21
21
|
|
22
22
|
struct _hash class_hash;
|
23
|
+
struct _hash str_hash;
|
24
|
+
struct _hash sym_hash;
|
23
25
|
struct _hash intern_hash;
|
24
26
|
|
25
27
|
// almost the Murmur hash algorithm
|
@@ -64,6 +66,8 @@ static uint32_t hash_calc(const uint8_t *key, size_t len) {
|
|
64
66
|
|
65
67
|
void oj_hash_init() {
|
66
68
|
memset(class_hash.slots, 0, sizeof(class_hash.slots));
|
69
|
+
memset(str_hash.slots, 0, sizeof(str_hash.slots));
|
70
|
+
memset(sym_hash.slots, 0, sizeof(sym_hash.slots));
|
67
71
|
memset(intern_hash.slots, 0, sizeof(intern_hash.slots));
|
68
72
|
}
|
69
73
|
|
@@ -100,8 +104,8 @@ static VALUE hash_get(Hash hash, const char *key, size_t len, VALUE **slotp, VAL
|
|
100
104
|
}
|
101
105
|
|
102
106
|
void oj_hash_print() {
|
103
|
-
|
104
|
-
KeyVal
|
107
|
+
uint32_t i;
|
108
|
+
KeyVal b;
|
105
109
|
|
106
110
|
for (i = 0; i < HASH_SLOT_CNT; i++) {
|
107
111
|
printf("%4d:", i);
|
@@ -112,11 +116,44 @@ void oj_hash_print() {
|
|
112
116
|
}
|
113
117
|
}
|
114
118
|
|
119
|
+
void oj_hash_sizes() {
|
120
|
+
uint32_t i;
|
121
|
+
KeyVal b;
|
122
|
+
int max = 0;
|
123
|
+
int min = 1000000;
|
124
|
+
|
125
|
+
for (i = 0; i < HASH_SLOT_CNT; i++) {
|
126
|
+
int cnt = 0;
|
127
|
+
|
128
|
+
for (b = str_hash.slots + i; 0 != b && 0 != b->key; b = b->next) {
|
129
|
+
cnt++;
|
130
|
+
}
|
131
|
+
// printf(" %4d\n", cnt);
|
132
|
+
if (max < cnt) {
|
133
|
+
max = cnt;
|
134
|
+
}
|
135
|
+
if (cnt < min) {
|
136
|
+
min = cnt;
|
137
|
+
}
|
138
|
+
}
|
139
|
+
printf("min: %d max: %d\n", min, max);
|
140
|
+
}
|
141
|
+
|
115
142
|
VALUE
|
116
143
|
oj_class_hash_get(const char *key, size_t len, VALUE **slotp) {
|
117
144
|
return hash_get(&class_hash, key, len, slotp, Qnil);
|
118
145
|
}
|
119
146
|
|
147
|
+
VALUE
|
148
|
+
oj_str_hash_get(const char *key, size_t len, VALUE **slotp) {
|
149
|
+
return hash_get(&str_hash, key, len, slotp, Qnil);
|
150
|
+
}
|
151
|
+
|
152
|
+
VALUE
|
153
|
+
oj_sym_hash_get(const char *key, size_t len, VALUE **slotp) {
|
154
|
+
return hash_get(&sym_hash, key, len, slotp, Qnil);
|
155
|
+
}
|
156
|
+
|
120
157
|
ID oj_attr_hash_get(const char *key, size_t len, ID **slotp) {
|
121
158
|
return (ID)hash_get(&intern_hash, key, len, (VALUE **)slotp, 0);
|
122
159
|
}
|
data/ext/oj/hash.h
CHANGED
@@ -11,6 +11,8 @@ typedef struct _hash *Hash;
|
|
11
11
|
extern void oj_hash_init();
|
12
12
|
|
13
13
|
extern VALUE oj_class_hash_get(const char *key, size_t len, VALUE **slotp);
|
14
|
+
extern VALUE oj_str_hash_get(const char *key, size_t len, VALUE **slotp);
|
15
|
+
extern VALUE oj_sym_hash_get(const char *key, size_t len, VALUE **slotp);
|
14
16
|
extern ID oj_attr_hash_get(const char *key, size_t len, ID **slotp);
|
15
17
|
|
16
18
|
extern void oj_hash_print();
|
data/ext/oj/mimic_json.c
CHANGED
@@ -389,9 +389,9 @@ static VALUE mimic_generate_core(int argc, VALUE *argv, Options copts) {
|
|
389
389
|
} else {
|
390
390
|
VALUE active_hack[1];
|
391
391
|
|
392
|
-
|
393
|
-
|
394
|
-
|
392
|
+
if (Qundef == state_class) {
|
393
|
+
oj_define_mimic_json(0, NULL, Qnil);
|
394
|
+
}
|
395
395
|
active_hack[0] = rb_funcall(state_class, oj_new_id, 0);
|
396
396
|
oj_dump_obj_to_json_using_params(*argv, copts, &out, 1, active_hack);
|
397
397
|
}
|
@@ -480,7 +480,7 @@ oj_mimic_pretty_generate(int argc, VALUE *argv, VALUE self) {
|
|
480
480
|
rb_hash_aset(h, oj_array_nl_sym, rb_str_new2("\n"));
|
481
481
|
}
|
482
482
|
if (Qundef == state_class) {
|
483
|
-
|
483
|
+
oj_define_mimic_json(0, NULL, Qnil);
|
484
484
|
}
|
485
485
|
rargs[1] = rb_funcall(state_class, oj_new_id, 1, h);
|
486
486
|
|
@@ -713,6 +713,8 @@ static struct _options mimic_object_to_json_options = {0, // indent
|
|
713
713
|
No, // safe
|
714
714
|
false, // sec_prec_set
|
715
715
|
No, // ignore_under
|
716
|
+
Yes, // cache_keys
|
717
|
+
3, // cache_str
|
716
718
|
0, // int_range_min
|
717
719
|
0, // int_range_max
|
718
720
|
oj_json_class, // create_id
|
data/ext/oj/object.c
CHANGED
@@ -30,11 +30,38 @@ inline static long read_long(const char *str, size_t len) {
|
|
30
30
|
|
31
31
|
static VALUE calc_hash_key(ParseInfo pi, Val kval, char k1) {
|
32
32
|
volatile VALUE rkey;
|
33
|
+
#if 0
|
34
|
+
VALUE *slot;
|
33
35
|
|
36
|
+
if (':' == k1) {
|
37
|
+
if (Qnil == (rkey = oj_sym_hash_get(kval->key + 1, kval->klen - 1, &slot))) {
|
38
|
+
rkey = rb_str_new(kval->key + 1, kval->klen - 1);
|
39
|
+
rkey = oj_encode(rkey);
|
40
|
+
rkey = rb_str_intern(rkey);
|
41
|
+
*slot = rkey;
|
42
|
+
rb_gc_register_address(slot);
|
43
|
+
}
|
44
|
+
} else if (Yes == pi->options.sym_key) {
|
45
|
+
if (Qnil == (rkey = oj_sym_hash_get(kval->key, kval->klen, &slot))) {
|
46
|
+
rkey = rb_str_new(kval->key, kval->klen);
|
47
|
+
rkey = oj_encode(rkey);
|
48
|
+
rkey = rb_str_intern(rkey);
|
49
|
+
*slot = rkey;
|
50
|
+
rb_gc_register_address(slot);
|
51
|
+
}
|
52
|
+
} else {
|
53
|
+
if (Qnil == (rkey = oj_str_hash_get(kval->key, kval->klen, &slot))) {
|
54
|
+
rkey = rb_str_new(kval->key, kval->klen);
|
55
|
+
rkey = oj_encode(rkey);
|
56
|
+
*slot = rkey;
|
57
|
+
rb_gc_register_address(slot);
|
58
|
+
}
|
59
|
+
}
|
60
|
+
#else
|
34
61
|
if (':' == k1) {
|
35
62
|
rkey = rb_str_new(kval->key + 1, kval->klen - 1);
|
36
63
|
rkey = oj_encode(rkey);
|
37
|
-
|
64
|
+
rkey = rb_str_intern(rkey);
|
38
65
|
} else {
|
39
66
|
rkey = rb_str_new(kval->key, kval->klen);
|
40
67
|
rkey = oj_encode(rkey);
|
@@ -42,6 +69,7 @@ static VALUE calc_hash_key(ParseInfo pi, Val kval, char k1) {
|
|
42
69
|
rkey = rb_str_intern(rkey);
|
43
70
|
}
|
44
71
|
}
|
72
|
+
#endif
|
45
73
|
return rkey;
|
46
74
|
}
|
47
75
|
|
data/ext/oj/oj.c
CHANGED
@@ -106,6 +106,8 @@ static VALUE auto_sym;
|
|
106
106
|
static VALUE bigdecimal_as_decimal_sym;
|
107
107
|
static VALUE bigdecimal_load_sym;
|
108
108
|
static VALUE bigdecimal_sym;
|
109
|
+
static VALUE cache_keys_sym;
|
110
|
+
static VALUE cache_str_sym;
|
109
111
|
static VALUE circular_sym;
|
110
112
|
static VALUE class_cache_sym;
|
111
113
|
static VALUE compat_bigdecimal_sym;
|
@@ -186,6 +188,8 @@ struct _options oj_default_options = {
|
|
186
188
|
No, // safe
|
187
189
|
false, // sec_prec_set
|
188
190
|
No, // ignore_under
|
191
|
+
Yes, // cache_keys
|
192
|
+
3, // cache_str
|
189
193
|
0, // int_range_min
|
190
194
|
0, // int_range_max
|
191
195
|
oj_json_class, // create_id
|
@@ -279,9 +283,11 @@ struct _options oj_default_options = {
|
|
279
283
|
*used
|
280
284
|
* - *:array_class* [_Class_|_nil_] Class to use instead of Array on load
|
281
285
|
* - *:omit_nil* [_true_|_false_] if true Hash and Object attributes with nil values are omitted
|
282
|
-
* - *:ignore* [_nil_|
|
283
|
-
* - *:ignore_under* [
|
286
|
+
* - *:ignore* [_nil_|_Array_] either nil or an Array of classes to ignore when dumping
|
287
|
+
* - *:ignore_under* [_Boolean_] if true then attributes that start with _ are ignored when dumping in
|
284
288
|
*object or custom mode.
|
289
|
+
* - *:cache_keys* [_Boolean_] if true then hash keys are cached
|
290
|
+
* - *:cache_str* [_Fixnum_] maximum string value length to cache
|
285
291
|
* - *:integer_range* [_Range_] Dump integers outside range as strings.
|
286
292
|
* - *:trace* [_true,_|_false_] Trace all load and dump calls, default is false (trace is off)
|
287
293
|
* - *:safe* [_true,_|_false_] Safe mimic breaks JSON mimic to be safer, default is false (safe is
|
@@ -389,11 +395,17 @@ static VALUE get_def_opts(VALUE self) {
|
|
389
395
|
? Qtrue
|
390
396
|
: ((No == oj_default_options.safe) ? Qfalse : Qnil));
|
391
397
|
rb_hash_aset(opts, float_prec_sym, INT2FIX(oj_default_options.float_prec));
|
398
|
+
rb_hash_aset(opts, cache_str_sym, INT2FIX(oj_default_options.cache_str));
|
392
399
|
rb_hash_aset(opts,
|
393
400
|
ignore_under_sym,
|
394
401
|
(Yes == oj_default_options.ignore_under)
|
395
402
|
? Qtrue
|
396
403
|
: ((No == oj_default_options.ignore_under) ? Qfalse : Qnil));
|
404
|
+
rb_hash_aset(opts,
|
405
|
+
cache_keys_sym,
|
406
|
+
(Yes == oj_default_options.cache_keys)
|
407
|
+
? Qtrue
|
408
|
+
: ((No == oj_default_options.cache_keys) ? Qfalse : Qnil));
|
397
409
|
switch (oj_default_options.mode) {
|
398
410
|
case StrictMode: rb_hash_aset(opts, mode_sym, strict_sym); break;
|
399
411
|
case CompatMode: rb_hash_aset(opts, mode_sym, compat_sym); break;
|
@@ -557,6 +569,8 @@ static VALUE get_def_opts(VALUE self) {
|
|
557
569
|
* - *:ignore* [_nil_|Array] either nil or an Array of classes to ignore when dumping
|
558
570
|
* - *:ignore_under* [_Boolean_] if true then attributes that start with _ are ignored when
|
559
571
|
*dumping in object or custom mode.
|
572
|
+
* - *:cache_keys* [_Boolean_] if true then hash keys are cached
|
573
|
+
* - *:cache_str* [_Fixnum_] maximum string vsalue length to cache
|
560
574
|
* - *:integer_range* [_Range_] Dump integers outside range as strings.
|
561
575
|
* - *:trace* [_Boolean_] turn trace on or off.
|
562
576
|
* - *:safe* [_Boolean_] turn safe mimic on or off.
|
@@ -589,6 +603,7 @@ void oj_parse_options(VALUE ropts, Options copts) {
|
|
589
603
|
{oj_safe_sym, &copts->safe},
|
590
604
|
{ignore_under_sym, &copts->ignore_under},
|
591
605
|
{oj_create_additions_sym, &copts->create_ok},
|
606
|
+
{cache_keys_sym, &copts->cache_keys},
|
592
607
|
{Qnil, 0}};
|
593
608
|
YesNoOpt o;
|
594
609
|
volatile VALUE v;
|
@@ -647,6 +662,28 @@ void oj_parse_options(VALUE ropts, Options copts) {
|
|
647
662
|
copts->float_prec = n;
|
648
663
|
}
|
649
664
|
}
|
665
|
+
if (Qnil != (v = rb_hash_lookup(ropts, cache_str_sym))) {
|
666
|
+
int n;
|
667
|
+
|
668
|
+
#ifdef RUBY_INTEGER_UNIFICATION
|
669
|
+
if (rb_cInteger != rb_obj_class(v)) {
|
670
|
+
rb_raise(rb_eArgError, ":cache_str must be a Integer.");
|
671
|
+
}
|
672
|
+
#else
|
673
|
+
if (T_FIXNUM != rb_type(v)) {
|
674
|
+
rb_raise(rb_eArgError, ":cache_str must be a Fixnum.");
|
675
|
+
}
|
676
|
+
#endif
|
677
|
+
n = FIX2INT(v);
|
678
|
+
if (0 >= n) {
|
679
|
+
copts->cache_str = 0;
|
680
|
+
} else {
|
681
|
+
if (32 < n) {
|
682
|
+
n = 32;
|
683
|
+
}
|
684
|
+
copts->cache_str = (char)n;
|
685
|
+
}
|
686
|
+
}
|
650
687
|
if (Qnil != (v = rb_hash_lookup(ropts, sec_prec_sym))) {
|
651
688
|
int n;
|
652
689
|
|
@@ -1636,13 +1673,20 @@ extern VALUE oj_optimize_rails(VALUE self);
|
|
1636
1673
|
|
1637
1674
|
/*
|
1638
1675
|
extern void oj_hash_test();
|
1639
|
-
|
1640
1676
|
static VALUE
|
1641
1677
|
hash_test(VALUE self) {
|
1642
1678
|
oj_hash_test();
|
1643
1679
|
return Qnil;
|
1644
1680
|
}
|
1645
1681
|
*/
|
1682
|
+
/*
|
1683
|
+
extern void oj_hash_sizes();
|
1684
|
+
static VALUE
|
1685
|
+
hash_test(VALUE self) {
|
1686
|
+
oj_hash_sizes();
|
1687
|
+
return Qnil;
|
1688
|
+
}
|
1689
|
+
*/
|
1646
1690
|
|
1647
1691
|
static VALUE protect_require(VALUE x) {
|
1648
1692
|
rb_require("time");
|
@@ -1816,6 +1860,10 @@ void Init_oj() {
|
|
1816
1860
|
rb_gc_register_address(&bigdecimal_load_sym);
|
1817
1861
|
bigdecimal_sym = ID2SYM(rb_intern("bigdecimal"));
|
1818
1862
|
rb_gc_register_address(&bigdecimal_sym);
|
1863
|
+
cache_keys_sym = ID2SYM(rb_intern("cache_keys"));
|
1864
|
+
rb_gc_register_address(&cache_keys_sym);
|
1865
|
+
cache_str_sym = ID2SYM(rb_intern("cache_str"));
|
1866
|
+
rb_gc_register_address(&cache_str_sym);
|
1819
1867
|
circular_sym = ID2SYM(rb_intern("circular"));
|
1820
1868
|
rb_gc_register_address(&circular_sym);
|
1821
1869
|
class_cache_sym = ID2SYM(rb_intern("class_cache"));
|
data/ext/oj/oj.h
CHANGED
@@ -143,6 +143,8 @@ typedef struct _options {
|
|
143
143
|
char safe; // YesNo
|
144
144
|
char sec_prec_set; // boolean (0 or 1)
|
145
145
|
char ignore_under; // YesNo - ignore attrs starting with _ if true in object and custom modes
|
146
|
+
char cache_keys; // YexNo
|
147
|
+
char cache_str; // string short than or equal to this are cache
|
146
148
|
int64_t int_range_min; // dump numbers below as string
|
147
149
|
int64_t int_range_max; // dump numbers above as string
|
148
150
|
const char * create_id; // 0 or string
|
data/ext/oj/parse.c
CHANGED
@@ -1129,7 +1129,7 @@ CLEANUP:
|
|
1129
1129
|
if (Qnil != pi->err_class) {
|
1130
1130
|
pi->err.clas = pi->err_class;
|
1131
1131
|
}
|
1132
|
-
if (CompatMode == pi->options.mode && Yes != pi->options.safe) {
|
1132
|
+
if ((CompatMode == pi->options.mode || RailsMode == pi->options.mode) && Yes != pi->options.safe) {
|
1133
1133
|
// The json gem requires the error message be UTF-8 encoded. In
|
1134
1134
|
// additional the complete JSON source must be returned. There
|
1135
1135
|
// does not seem to be a size limit.
|
data/ext/oj/parse.h
CHANGED
@@ -90,6 +90,9 @@ extern void oj_set_wab_callbacks(ParseInfo pi);
|
|
90
90
|
extern void oj_sparse2(ParseInfo pi);
|
91
91
|
extern VALUE oj_pi_sparse(int argc, VALUE *argv, ParseInfo pi, int fd);
|
92
92
|
|
93
|
+
extern VALUE oj_cstr_to_value(const char *str, size_t len, size_t cache_str);
|
94
|
+
extern VALUE oj_calc_hash_key(ParseInfo pi, Val parent);
|
95
|
+
|
93
96
|
static inline void parse_info_init(ParseInfo pi) {
|
94
97
|
memset(pi, 0, sizeof(struct _parseInfo));
|
95
98
|
}
|
data/ext/oj/rails.c
CHANGED
@@ -828,7 +828,8 @@ rails_mimic_json(VALUE self) {
|
|
828
828
|
json = rb_define_module("JSON");
|
829
829
|
}
|
830
830
|
oj_mimic_json_methods(json);
|
831
|
-
//
|
831
|
+
// Setting the default mode breaks the prmoise in the docs not to.
|
832
|
+
//oj_default_options.mode = RailsMode;
|
832
833
|
|
833
834
|
return Qnil;
|
834
835
|
}
|
data/ext/oj/scp.c
CHANGED
@@ -9,6 +9,7 @@
|
|
9
9
|
#include <unistd.h>
|
10
10
|
|
11
11
|
#include "encode.h"
|
12
|
+
#include "hash.h"
|
12
13
|
#include "oj.h"
|
13
14
|
#include "parse.h"
|
14
15
|
|
@@ -82,19 +83,6 @@ static void end_array(ParseInfo pi) {
|
|
82
83
|
rb_funcall(pi->handler, oj_array_end_id, 0);
|
83
84
|
}
|
84
85
|
|
85
|
-
static VALUE calc_hash_key(ParseInfo pi, Val kval) {
|
86
|
-
volatile VALUE rkey = kval->key_val;
|
87
|
-
|
88
|
-
if (Qundef == rkey) {
|
89
|
-
rkey = rb_str_new(kval->key, kval->klen);
|
90
|
-
rkey = oj_encode(rkey);
|
91
|
-
if (Yes == pi->options.sym_key) {
|
92
|
-
rkey = rb_str_intern(rkey);
|
93
|
-
}
|
94
|
-
}
|
95
|
-
return rkey;
|
96
|
-
}
|
97
|
-
|
98
86
|
static VALUE hash_key(ParseInfo pi, const char *key, size_t klen) {
|
99
87
|
return rb_funcall(pi->handler, oj_hash_key_id, 1, rb_str_new(key, klen));
|
100
88
|
}
|
@@ -107,7 +95,7 @@ static void hash_set_cstr(ParseInfo pi, Val kval, const char *str, size_t len, c
|
|
107
95
|
oj_hash_set_id,
|
108
96
|
3,
|
109
97
|
stack_peek(&pi->stack)->val,
|
110
|
-
|
98
|
+
oj_calc_hash_key(pi, kval),
|
111
99
|
rstr);
|
112
100
|
}
|
113
101
|
|
@@ -116,7 +104,7 @@ static void hash_set_num(ParseInfo pi, Val kval, NumInfo ni) {
|
|
116
104
|
oj_hash_set_id,
|
117
105
|
3,
|
118
106
|
stack_peek(&pi->stack)->val,
|
119
|
-
|
107
|
+
oj_calc_hash_key(pi, kval),
|
120
108
|
oj_num_as_value(ni));
|
121
109
|
}
|
122
110
|
|
@@ -125,7 +113,7 @@ static void hash_set_value(ParseInfo pi, Val kval, VALUE value) {
|
|
125
113
|
oj_hash_set_id,
|
126
114
|
3,
|
127
115
|
stack_peek(&pi->stack)->val,
|
128
|
-
|
116
|
+
oj_calc_hash_key(pi, kval),
|
129
117
|
value);
|
130
118
|
}
|
131
119
|
|
data/ext/oj/strict.c
CHANGED
@@ -8,10 +8,65 @@
|
|
8
8
|
|
9
9
|
#include "encode.h"
|
10
10
|
#include "err.h"
|
11
|
+
#include "hash.h"
|
11
12
|
#include "oj.h"
|
12
13
|
#include "parse.h"
|
13
14
|
#include "trace.h"
|
14
15
|
|
16
|
+
VALUE oj_cstr_to_value(const char *str, size_t len, size_t cache_str) {
|
17
|
+
volatile VALUE rstr = Qnil;
|
18
|
+
|
19
|
+
if (len <= cache_str) {
|
20
|
+
VALUE *slot;
|
21
|
+
|
22
|
+
if (Qnil == (rstr = oj_str_hash_get(str, len, &slot))) {
|
23
|
+
rstr = rb_str_new(str, len);
|
24
|
+
rstr = oj_encode(rstr);
|
25
|
+
*slot = rstr;
|
26
|
+
rb_gc_register_address(slot);
|
27
|
+
}
|
28
|
+
} else {
|
29
|
+
rstr = rb_str_new(str, len);
|
30
|
+
rstr = oj_encode(rstr);
|
31
|
+
}
|
32
|
+
return rstr;
|
33
|
+
}
|
34
|
+
|
35
|
+
VALUE oj_calc_hash_key(ParseInfo pi, Val parent) {
|
36
|
+
volatile VALUE rkey = parent->key_val;
|
37
|
+
|
38
|
+
if (Qundef != rkey) {
|
39
|
+
return rkey;
|
40
|
+
}
|
41
|
+
if (Yes != pi->options.cache_keys) {
|
42
|
+
rkey = rb_str_new(parent->key, parent->klen);
|
43
|
+
rkey = oj_encode(rkey);
|
44
|
+
if (Yes == pi->options.sym_key) {
|
45
|
+
rkey = rb_str_intern(rkey);
|
46
|
+
}
|
47
|
+
return rkey;
|
48
|
+
}
|
49
|
+
VALUE *slot;
|
50
|
+
|
51
|
+
if (Yes == pi->options.sym_key) {
|
52
|
+
if (Qnil == (rkey = oj_sym_hash_get(parent->key, parent->klen, &slot))) {
|
53
|
+
rkey = rb_str_new(parent->key, parent->klen);
|
54
|
+
rkey = oj_encode(rkey);
|
55
|
+
rkey = rb_str_intern(rkey);
|
56
|
+
*slot = rkey;
|
57
|
+
rb_gc_register_address(slot);
|
58
|
+
}
|
59
|
+
} else {
|
60
|
+
if (Qnil == (rkey = oj_str_hash_get(parent->key, parent->klen, &slot))) {
|
61
|
+
rkey = rb_str_new(parent->key, parent->klen);
|
62
|
+
rkey = oj_encode(rkey);
|
63
|
+
*slot = rkey;
|
64
|
+
rb_gc_register_address(slot);
|
65
|
+
}
|
66
|
+
}
|
67
|
+
return rkey;
|
68
|
+
}
|
69
|
+
|
15
70
|
static void hash_end(ParseInfo pi) {
|
16
71
|
if (Yes == pi->options.trace) {
|
17
72
|
oj_trace_parse_hash_end(pi, __FILE__, __LINE__);
|
@@ -36,9 +91,8 @@ static void add_value(ParseInfo pi, VALUE val) {
|
|
36
91
|
}
|
37
92
|
|
38
93
|
static void add_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
|
39
|
-
volatile VALUE rstr =
|
94
|
+
volatile VALUE rstr = oj_cstr_to_value(str, len, (size_t)pi->options.cache_str);
|
40
95
|
|
41
|
-
rstr = oj_encode(rstr);
|
42
96
|
pi->stack.head->val = rstr;
|
43
97
|
if (Yes == pi->options.trace) {
|
44
98
|
oj_trace_parse_call("add_string", pi, __FILE__, __LINE__, rstr);
|
@@ -65,24 +119,12 @@ static VALUE start_hash(ParseInfo pi) {
|
|
65
119
|
return rb_hash_new();
|
66
120
|
}
|
67
121
|
|
68
|
-
static VALUE calc_hash_key(ParseInfo pi, Val parent) {
|
69
|
-
volatile VALUE rkey = parent->key_val;
|
70
|
-
|
71
|
-
if (Qundef == rkey) {
|
72
|
-
rkey = rb_str_new(parent->key, parent->klen);
|
73
|
-
}
|
74
|
-
rkey = oj_encode(rkey);
|
75
|
-
if (Yes == pi->options.sym_key) {
|
76
|
-
rkey = rb_str_intern(rkey);
|
77
|
-
}
|
78
|
-
return rkey;
|
79
|
-
}
|
80
|
-
|
81
122
|
static void hash_set_cstr(ParseInfo pi, Val parent, const char *str, size_t len, const char *orig) {
|
82
|
-
volatile VALUE rstr =
|
123
|
+
volatile VALUE rstr = oj_cstr_to_value(str, len, (size_t)pi->options.cache_str);
|
83
124
|
|
84
|
-
|
85
|
-
|
125
|
+
rb_hash_aset(stack_peek(&pi->stack)->val,
|
126
|
+
oj_calc_hash_key(pi, parent),
|
127
|
+
rstr);
|
86
128
|
if (Yes == pi->options.trace) {
|
87
129
|
oj_trace_parse_call("set_string", pi, __FILE__, __LINE__, rstr);
|
88
130
|
}
|
@@ -95,14 +137,18 @@ static void hash_set_num(ParseInfo pi, Val parent, NumInfo ni) {
|
|
95
137
|
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value");
|
96
138
|
}
|
97
139
|
v = oj_num_as_value(ni);
|
98
|
-
rb_hash_aset(stack_peek(&pi->stack)->val,
|
140
|
+
rb_hash_aset(stack_peek(&pi->stack)->val,
|
141
|
+
oj_calc_hash_key(pi, parent),
|
142
|
+
v);
|
99
143
|
if (Yes == pi->options.trace) {
|
100
144
|
oj_trace_parse_call("set_number", pi, __FILE__, __LINE__, v);
|
101
145
|
}
|
102
146
|
}
|
103
147
|
|
104
148
|
static void hash_set_value(ParseInfo pi, Val parent, VALUE value) {
|
105
|
-
rb_hash_aset(stack_peek(&pi->stack)->val,
|
149
|
+
rb_hash_aset(stack_peek(&pi->stack)->val,
|
150
|
+
oj_calc_hash_key(pi, parent),
|
151
|
+
value);
|
106
152
|
if (Yes == pi->options.trace) {
|
107
153
|
oj_trace_parse_call("set_value", pi, __FILE__, __LINE__, value);
|
108
154
|
}
|
@@ -116,9 +162,8 @@ static VALUE start_array(ParseInfo pi) {
|
|
116
162
|
}
|
117
163
|
|
118
164
|
static void array_append_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
|
119
|
-
volatile VALUE rstr =
|
165
|
+
volatile VALUE rstr = oj_cstr_to_value(str, len, (size_t)pi->options.cache_str);
|
120
166
|
|
121
|
-
rstr = oj_encode(rstr);
|
122
167
|
rb_ary_push(stack_peek(&pi->stack)->val, rstr);
|
123
168
|
if (Yes == pi->options.trace) {
|
124
169
|
oj_trace_parse_call("append_string", pi, __FILE__, __LINE__, rstr);
|
data/ext/oj/wab.c
CHANGED
@@ -10,6 +10,7 @@
|
|
10
10
|
#include "dump.h"
|
11
11
|
#include "encode.h"
|
12
12
|
#include "err.h"
|
13
|
+
#include "hash.h"
|
13
14
|
#include "oj.h"
|
14
15
|
#include "parse.h"
|
15
16
|
#include "trace.h"
|
@@ -292,6 +293,34 @@ void oj_dump_wab_val(VALUE obj, int depth, Out out) {
|
|
292
293
|
|
293
294
|
///// load functions /////
|
294
295
|
|
296
|
+
static VALUE calc_hash_key(ParseInfo pi, Val parent) {
|
297
|
+
volatile VALUE rkey = parent->key_val;
|
298
|
+
|
299
|
+
if (Qundef != rkey) {
|
300
|
+
rkey = oj_encode(rkey);
|
301
|
+
rkey = rb_str_intern(rkey);
|
302
|
+
|
303
|
+
return rkey;
|
304
|
+
}
|
305
|
+
if (Yes != pi->options.cache_keys) {
|
306
|
+
rkey = rb_str_new(parent->key, parent->klen);
|
307
|
+
rkey = oj_encode(rkey);
|
308
|
+
rkey = rb_str_intern(rkey);
|
309
|
+
|
310
|
+
return rkey;
|
311
|
+
}
|
312
|
+
VALUE *slot;
|
313
|
+
|
314
|
+
if (Qnil == (rkey = oj_sym_hash_get(parent->key, parent->klen, &slot))) {
|
315
|
+
rkey = rb_str_new(parent->key, parent->klen);
|
316
|
+
rkey = oj_encode(rkey);
|
317
|
+
rkey = rb_str_intern(rkey);
|
318
|
+
*slot = rkey;
|
319
|
+
rb_gc_register_address(slot);
|
320
|
+
}
|
321
|
+
return rkey;
|
322
|
+
}
|
323
|
+
|
295
324
|
static void hash_end(ParseInfo pi) {
|
296
325
|
if (Yes == pi->options.trace) {
|
297
326
|
oj_trace_parse_hash_end(pi, __FILE__, __LINE__);
|
@@ -432,7 +461,7 @@ static VALUE protect_uri(VALUE rstr) {
|
|
432
461
|
return rb_funcall(resolve_uri_class(), oj_parse_id, 1, rstr);
|
433
462
|
}
|
434
463
|
|
435
|
-
static VALUE cstr_to_rstr(const char *str, size_t len) {
|
464
|
+
static VALUE cstr_to_rstr(ParseInfo pi, const char *str, size_t len) {
|
436
465
|
volatile VALUE v = Qnil;
|
437
466
|
|
438
467
|
if (30 == len && '-' == str[4] && '-' == str[7] && 'T' == str[10] && ':' == str[13] &&
|
@@ -445,20 +474,20 @@ static VALUE cstr_to_rstr(const char *str, size_t len) {
|
|
445
474
|
uuid_check(str, (int)len) && Qnil != resolve_wab_uuid_class()) {
|
446
475
|
return rb_funcall(wab_uuid_clas, oj_new_id, 1, rb_str_new(str, len));
|
447
476
|
}
|
448
|
-
v = rb_str_new(str, len);
|
449
477
|
if (7 < len && 0 == strncasecmp("http://", str, 7)) {
|
450
478
|
int err = 0;
|
479
|
+
v = rb_str_new(str, len);
|
451
480
|
volatile VALUE uri = rb_protect(protect_uri, v, &err);
|
452
481
|
|
453
482
|
if (0 == err) {
|
454
483
|
return uri;
|
455
484
|
}
|
456
485
|
}
|
457
|
-
return
|
486
|
+
return oj_cstr_to_value(str, len, (size_t)pi->options.cache_str);
|
458
487
|
}
|
459
488
|
|
460
489
|
static void add_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
|
461
|
-
pi->stack.head->val = cstr_to_rstr(str, len);
|
490
|
+
pi->stack.head->val = cstr_to_rstr(pi, str, len);
|
462
491
|
if (Yes == pi->options.trace) {
|
463
492
|
oj_trace_parse_call("add_string", pi, __FILE__, __LINE__, pi->stack.head->val);
|
464
493
|
}
|
@@ -484,20 +513,8 @@ static VALUE start_hash(ParseInfo pi) {
|
|
484
513
|
return rb_hash_new();
|
485
514
|
}
|
486
515
|
|
487
|
-
static VALUE calc_hash_key(ParseInfo pi, Val parent) {
|
488
|
-
volatile VALUE rkey = parent->key_val;
|
489
|
-
|
490
|
-
if (Qundef == rkey) {
|
491
|
-
rkey = rb_str_new(parent->key, parent->klen);
|
492
|
-
}
|
493
|
-
rkey = oj_encode(rkey);
|
494
|
-
rkey = rb_str_intern(rkey);
|
495
|
-
|
496
|
-
return rkey;
|
497
|
-
}
|
498
|
-
|
499
516
|
static void hash_set_cstr(ParseInfo pi, Val parent, const char *str, size_t len, const char *orig) {
|
500
|
-
volatile VALUE rval = cstr_to_rstr(str, len);
|
517
|
+
volatile VALUE rval = cstr_to_rstr(pi, str, len);
|
501
518
|
|
502
519
|
rb_hash_aset(stack_peek(&pi->stack)->val, calc_hash_key(pi, parent), rval);
|
503
520
|
if (Yes == pi->options.trace) {
|
@@ -533,7 +550,7 @@ static VALUE start_array(ParseInfo pi) {
|
|
533
550
|
}
|
534
551
|
|
535
552
|
static void array_append_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
|
536
|
-
volatile VALUE rval = cstr_to_rstr(str, len);
|
553
|
+
volatile VALUE rval = cstr_to_rstr(pi, str, len);
|
537
554
|
|
538
555
|
rb_ary_push(stack_peek(&pi->stack)->val, rval);
|
539
556
|
if (Yes == pi->options.trace) {
|
data/lib/oj/mimic.rb
CHANGED
data/lib/oj/version.rb
CHANGED
data/test/foo.rb
CHANGED
@@ -8,48 +8,13 @@ end
|
|
8
8
|
|
9
9
|
require 'oj'
|
10
10
|
|
11
|
-
|
12
|
-
def initialize
|
13
|
-
@x = 123
|
14
|
-
end
|
11
|
+
Oj.default_options = {cache_keys: false, cache_str: 0}
|
15
12
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
def initialize
|
23
|
-
@x = 321
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
foo = Foo.new
|
28
|
-
bar = Bar.new
|
29
|
-
|
30
|
-
require 'json'
|
31
|
-
|
32
|
-
puts "JSON: #{JSON.generate(foo)}"
|
33
|
-
puts "to_json: #{foo.to_json}"
|
34
|
-
puts "bar JSON: #{JSON.generate(bar)}"
|
35
|
-
puts "bar to_json: #{bar.to_json}"
|
36
|
-
|
37
|
-
m = bar.method('to_json')
|
38
|
-
puts "*** method: #{m} owner: #{m.owner.name}"
|
39
|
-
|
40
|
-
puts "---- rails"
|
41
|
-
require 'rails'
|
42
|
-
|
43
|
-
m = bar.method('to_json')
|
44
|
-
puts "*** method: #{m} owner: #{m.owner} params: #{m.parameters}"
|
45
|
-
|
46
|
-
puts "JSON: #{JSON.generate(foo)}"
|
47
|
-
puts "to_json: #{foo.to_json}"
|
48
|
-
|
49
|
-
puts "---- Oj.mimic_JSON"
|
50
|
-
Oj.mimic_JSON()
|
51
|
-
puts "Oj JSON: #{JSON.generate(foo)}"
|
52
|
-
puts "Oj to_json: #{foo.to_json}"
|
13
|
+
[:compat, :strict, :object].each { |mode|
|
14
|
+
GC.start
|
15
|
+
t = Time.now
|
16
|
+
Oj.load_file('pt_lemma_lookup.json', mode: mode)
|
17
|
+
puts "#{mode}: #{Time.now - t}"
|
18
|
+
}
|
53
19
|
|
54
|
-
|
55
|
-
puts "*** method: #{m} owner: #{m.owner} params: #{m.parameters}"
|
20
|
+
# check saj and scp
|
data/test/perf.rb
CHANGED
data/test/perf_scp.rb
CHANGED
@@ -14,16 +14,16 @@ require 'oj'
|
|
14
14
|
|
15
15
|
$verbose = false
|
16
16
|
$indent = 0
|
17
|
-
$iter =
|
17
|
+
$iter = 50_000
|
18
18
|
$with_bignum = false
|
19
|
-
$size =
|
19
|
+
$size = 1
|
20
20
|
|
21
21
|
opts = OptionParser.new
|
22
22
|
opts.on("-v", "verbose") { $verbose = true }
|
23
23
|
opts.on("-c", "--count [Int]", Integer, "iterations") { |i| $iter = i }
|
24
24
|
opts.on("-i", "--indent [Int]", Integer, "indentation") { |i| $indent = i }
|
25
|
-
opts.on("-s", "--size [Int]", Integer, "size (~Kbytes)")
|
26
|
-
opts.on("-b", "with bignum")
|
25
|
+
opts.on("-s", "--size [Int]", Integer, "size (~Kbytes)") { |i| $size = i }
|
26
|
+
opts.on("-b", "with bignum") { $with_bignum = true }
|
27
27
|
opts.on("-h", "--help", "Show this display") { puts opts; Process.exit!(0) }
|
28
28
|
files = opts.parse(ARGV)
|
29
29
|
|
@@ -47,7 +47,7 @@ if 0 < $size
|
|
47
47
|
end
|
48
48
|
end
|
49
49
|
|
50
|
-
Oj.default_options = { :indent => $indent, :mode => :
|
50
|
+
Oj.default_options = { :indent => $indent, :mode => :strict, cache_keys: true, cache_str: 5 }
|
51
51
|
|
52
52
|
$json = Oj.dump($obj)
|
53
53
|
$failed = {} # key is same as String used in tests later
|
@@ -105,7 +105,7 @@ class AllHandler < Oj::ScHandler
|
|
105
105
|
|
106
106
|
def hash_set(h, key, value)
|
107
107
|
end
|
108
|
-
|
108
|
+
|
109
109
|
def array_append(a, value)
|
110
110
|
end
|
111
111
|
|
@@ -137,10 +137,11 @@ end
|
|
137
137
|
puts '-' * 80
|
138
138
|
puts "Parse Performance"
|
139
139
|
perf = Perf.new()
|
140
|
-
perf.add('Oj::Saj', 'all') { Oj.saj_parse(saj_handler, $json) }
|
141
|
-
perf.add('Oj::Saj', 'none') { Oj.saj_parse(no_saj, $json) }
|
142
|
-
perf.add('Oj::Scp', 'all') { Oj.sc_parse(sc_handler, $json) }
|
143
|
-
perf.add('Oj::Scp', 'none') { Oj.sc_parse(no_handler, $json) }
|
140
|
+
perf.add('Oj::Saj.all', 'all') { Oj.saj_parse(saj_handler, $json) }
|
141
|
+
perf.add('Oj::Saj.none', 'none') { Oj.saj_parse(no_saj, $json) }
|
142
|
+
perf.add('Oj::Scp.all', 'all') { Oj.sc_parse(sc_handler, $json) }
|
143
|
+
perf.add('Oj::Scp.none', 'none') { Oj.sc_parse(no_handler, $json) }
|
144
|
+
perf.add('Oj::load', 'none') { Oj.wab_load($json) }
|
144
145
|
perf.add('Yajl', 'parse') { Yajl::Parser.parse($json) } unless $failed.has_key?('Yajl')
|
145
146
|
perf.add('JSON::Ext', 'parse') { JSON::Ext::Parser.new($json).parse } unless $failed.has_key?('JSON::Ext')
|
146
147
|
perf.run($iter)
|
data/test/perf_strict.rb
CHANGED
@@ -15,6 +15,8 @@ $iter = 20000
|
|
15
15
|
$with_bignum = false
|
16
16
|
$with_nums = true
|
17
17
|
$size = 0
|
18
|
+
$symbolize = false
|
19
|
+
$cache_keys = true
|
18
20
|
|
19
21
|
opts = OptionParser.new
|
20
22
|
opts.on("-v", "verbose") { $verbose = true }
|
@@ -23,6 +25,8 @@ opts.on("-i", "--indent [Int]", Integer, "indentation") { |i| $indent = i }
|
|
23
25
|
opts.on("-s", "--size [Int]", Integer, "size (~Kbytes)") { |i| $size = i }
|
24
26
|
opts.on("-b", "with bignum") { $with_bignum = true }
|
25
27
|
opts.on("-n", "without numbers") { $with_nums = false }
|
28
|
+
opts.on("-z", "--symbolize", "symbolize keys") { $symbolize = true }
|
29
|
+
opts.on("-k", "--no-cache", "turn off key caching") { $cache_keys = false }
|
26
30
|
opts.on("-h", "--help", "Show this display") { puts opts; Process.exit!(0) }
|
27
31
|
files = opts.parse(ARGV)
|
28
32
|
|
@@ -51,7 +55,7 @@ else
|
|
51
55
|
}
|
52
56
|
end
|
53
57
|
|
54
|
-
Oj.default_options = { :indent => $indent, :mode => :strict }
|
58
|
+
Oj.default_options = { :indent => $indent, :mode => :strict, cache_keys: $cache_keys, cache_str: 5 }
|
55
59
|
|
56
60
|
if 0 < $size
|
57
61
|
o = $obj
|
@@ -62,9 +66,6 @@ if 0 < $size
|
|
62
66
|
end
|
63
67
|
|
64
68
|
$json = Oj.dump($obj)
|
65
|
-
$obj_json = Oj.dump($obj, :mode => :object)
|
66
|
-
#puts "*** size: #{$obj_json.size}"
|
67
|
-
#puts "*** #{$obj_json}"
|
68
69
|
$failed = {} # key is same as String used in tests later
|
69
70
|
|
70
71
|
def capture_error(tag, orig, load_key, dump_key, &blk)
|
@@ -77,8 +78,13 @@ def capture_error(tag, orig, load_key, dump_key, &blk)
|
|
77
78
|
end
|
78
79
|
|
79
80
|
# Verify that all packages dump and load correctly and return the same Object as the original.
|
80
|
-
capture_error('Oj:strict', $obj, 'load', 'dump') { |o|
|
81
|
-
|
81
|
+
capture_error('Oj:strict', $obj, 'load', 'dump') { |o|
|
82
|
+
Oj.strict_load(Oj.dump(o))
|
83
|
+
}
|
84
|
+
capture_error('Yajl', $obj, 'encode', 'parse') { |o|
|
85
|
+
require 'yajl'
|
86
|
+
Yajl::Parser.parse(Yajl::Encoder.encode(o))
|
87
|
+
}
|
82
88
|
capture_error('JSON::Ext', $obj, 'generate', 'parse') { |o|
|
83
89
|
require 'json'
|
84
90
|
require 'json/ext'
|
@@ -86,16 +92,11 @@ capture_error('JSON::Ext', $obj, 'generate', 'parse') { |o|
|
|
86
92
|
JSON.parser = JSON::Ext::Parser
|
87
93
|
JSON.parse(JSON.generate(o))
|
88
94
|
}
|
89
|
-
|
90
|
-
|
91
|
-
JSON.generator = JSON::Pure::Generator
|
92
|
-
JSON.parser = JSON::Pure::Parser
|
93
|
-
JSON.parse(JSON.generate(o))
|
94
|
-
}
|
95
|
+
|
96
|
+
Oj.default_options = { symbol_keys: $symbolize }
|
95
97
|
|
96
98
|
if $verbose
|
97
99
|
puts "json:\n#{$json}\n"
|
98
|
-
puts "object json:\n#{$obj_json}\n"
|
99
100
|
puts "Oj loaded object:\n#{Oj.strict_load($json)}\n"
|
100
101
|
puts "Yajl loaded object:\n#{Yajl::Parser.parse($json)}\n"
|
101
102
|
puts "JSON loaded object:\n#{JSON::Ext::Parser.new($json).parse}\n"
|
@@ -105,15 +106,12 @@ puts '-' * 80
|
|
105
106
|
puts "Strict Parse Performance"
|
106
107
|
perf = Perf.new()
|
107
108
|
unless $failed.has_key?('JSON::Ext')
|
108
|
-
perf.add('JSON::Ext', 'parse') { JSON.parse($json) }
|
109
|
+
perf.add('JSON::Ext', 'parse') { JSON.parse($json, symbolize_names: $symbolize) }
|
109
110
|
perf.before('JSON::Ext') { JSON.parser = JSON::Ext::Parser }
|
110
111
|
end
|
111
|
-
unless $failed.has_key?('JSON::Pure')
|
112
|
-
perf.add('JSON::Pure', 'parse') { JSON.parse($json) }
|
113
|
-
perf.before('JSON::Pure') { JSON.parser = JSON::Pure::Parser }
|
114
|
-
end
|
115
112
|
unless $failed.has_key?('Oj:strict')
|
116
113
|
perf.add('Oj:strict', 'strict_load') { Oj.strict_load($json) }
|
114
|
+
perf.add('Oj:wab', 'wab_load') { Oj.wab_load($json) }
|
117
115
|
end
|
118
116
|
perf.add('Yajl', 'parse') { Yajl::Parser.parse($json) } unless $failed.has_key?('Yajl')
|
119
117
|
perf.run($iter)
|
@@ -125,12 +123,8 @@ unless $failed.has_key?('JSON::Ext')
|
|
125
123
|
perf.add('JSON::Ext', 'dump') { JSON.generate($obj) }
|
126
124
|
perf.before('JSON::Ext') { JSON.generator = JSON::Ext::Generator }
|
127
125
|
end
|
128
|
-
unless $failed.has_key?('JSON::Pure')
|
129
|
-
perf.add('JSON::Pure', 'generate') { JSON.generate($obj) }
|
130
|
-
perf.before('JSON::Pure') { JSON.generator = JSON::Pure::Generator }
|
131
|
-
end
|
132
126
|
unless $failed.has_key?('Oj:strict')
|
133
|
-
perf.add('Oj:strict', 'dump') { Oj.dump($obj
|
127
|
+
perf.add('Oj:strict', 'dump') { Oj.dump($obj) }
|
134
128
|
end
|
135
129
|
perf.add('Yajl', 'encode') { Yajl::Encoder.encode($obj) } unless $failed.has_key?('Yajl')
|
136
130
|
perf.run($iter)
|
data/test/test_fast.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
# encoding:
|
2
|
+
# encoding: utf-8
|
3
3
|
|
4
4
|
$: << File.dirname(__FILE__)
|
5
5
|
|
@@ -279,10 +279,26 @@ class DocTest < Minitest::Test
|
|
279
279
|
['/array/1', {'num' => 3, 'string' => 'message', 'hash' => {'h2' => {'a' => [1, 2, 3]}}}],
|
280
280
|
['/array', [{'num' => 3, 'string' => 'message', 'hash' => {'h2' => {'a' => [1, 2, 3]}}}]],
|
281
281
|
['/', {'array' => [{'num' => 3, 'string' => 'message', 'hash' => {'h2' => {'a' => [1, 2, 3]}}}], 'boolean' => true}],
|
282
|
+
['/nothing', nil],
|
283
|
+
['/array/10', nil],
|
282
284
|
].each do |path,val|
|
283
|
-
|
285
|
+
if val.nil?
|
286
|
+
assert_nil(doc.fetch(path))
|
287
|
+
else
|
288
|
+
assert_equal(val, doc.fetch(path))
|
289
|
+
end
|
284
290
|
end
|
285
291
|
end
|
292
|
+
# verify empty hash and arrays return nil when a member is requested
|
293
|
+
Oj::Doc.open('{}') do |doc|
|
294
|
+
assert_nil(doc.fetch('/x'))
|
295
|
+
assert_nil(doc.fetch('/0'))
|
296
|
+
end
|
297
|
+
Oj::Doc.open('[]') do |doc|
|
298
|
+
assert_nil(doc.fetch('/x'))
|
299
|
+
assert_nil(doc.fetch('/0'))
|
300
|
+
end
|
301
|
+
|
286
302
|
end
|
287
303
|
|
288
304
|
def test_move_fetch_path
|
@@ -297,6 +313,20 @@ class DocTest < Minitest::Test
|
|
297
313
|
end
|
298
314
|
end
|
299
315
|
|
316
|
+
def test_exisits
|
317
|
+
Oj::Doc.open(@json1) do |doc|
|
318
|
+
[['/array/1', true],
|
319
|
+
['/array/1', true],
|
320
|
+
['/array/1/hash', true],
|
321
|
+
['/array/1/dash', false],
|
322
|
+
['/array/3', false],
|
323
|
+
['/nothing', false],
|
324
|
+
].each do |path,val|
|
325
|
+
assert_equal(val, doc.exists?(path))
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
300
330
|
def test_home
|
301
331
|
Oj::Doc.open(@json1) do |doc|
|
302
332
|
doc.move('/array/1/num')
|
data/test/test_various.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: oj
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.12.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Peter Ohler
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-07-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake-compiler
|