oj 3.11.5 → 3.12.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 964a9272c141179cf6f61317e89bc8dab43184b0d143355dc2212b89f123ccad
4
- data.tar.gz: 53a3b3cd773a87953cec71a649f491682b022c13d56ebcc4eb5653bc9cbb560e
3
+ metadata.gz: 7316e25014cf4268799f3f1e4a01a74fada832b1d68bf64d82cbeb42b2caf1dd
4
+ data.tar.gz: a02fdb9676efeacfec86475ff35afdb88cfef33e0c94188355cad69401ee2304
5
5
  SHA512:
6
- metadata.gz: 947dad8a5cfc4d5adb3efbd40218439c984ae6084ef304f6b3012e26e047438a03b6f149b7c224982fc8fd0a699b56fa0e6b9a174e8b00d6567dd911f601f227
7
- data.tar.gz: 536b05addb483b631883ccfb9269c6cd93b3e988ea9845021be3e2383a708974ff4e63a9d64a1a76bd8a9934969c42aa16495eccd75e88b7cf4704f619e7b693
6
+ metadata.gz: 8f83d20545998db46a7dd0bd0e2560f47198fbb0f192bc80f17ffbce046e38727e0c143e022f8d00ba2332e2cb0be27ddda294b6a96d6f4b091ec95e282d2f01
7
+ data.tar.gz: e62a125cf992f510296bbe6b3eeeaaadaa8ccd2891b764a290518f785b706a797af7fbd247ac360364cd610e67ad038488d6eefb55e4dc028a3b20540995e4df
data/README.md CHANGED
@@ -1,6 +1,10 @@
1
1
  # [![{}j](http://www.ohler.com/dev/images/oj_comet_64.svg)](http://www.ohler.com/oj) gem
2
2
 
3
- [![Build Status](https://img.shields.io/travis/ohler55/oj/master.svg?logo=travis)](http://travis-ci.org/ohler55/oj?branch=master) ![Gem](https://img.shields.io/gem/v/oj.svg) ![Gem](https://img.shields.io/gem/dt/oj.svg) [![SemVer compatibility](https://api.dependabot.com/badges/compatibility_score?dependency-name=oj&package-manager=bundler&version-scheme=semver)](https://dependabot.com/compatibility-score.html?dependency-name=oj&package-manager=bundler&version-scheme=semver) [![TideLift](https://tidelift.com/badges/github/ohler55/oj)](https://tidelift.com/subscription/pkg/rubygems-oj?utm_source=rubygems-oj&utm_medium=referral&utm_campaign=readme)
3
+ [![Build Status](https://img.shields.io/github/workflow/status/ohler55/oj/CI?logo=github)](https://github.com/ohler55/oj/actions/workflows/CI.yml)
4
+ ![Gem](https://img.shields.io/gem/v/oj.svg)
5
+ ![Gem](https://img.shields.io/gem/dt/oj.svg)
6
+ [![SemVer compatibility](https://api.dependabot.com/badges/compatibility_score?dependency-name=oj&package-manager=bundler&version-scheme=semver)](https://dependabot.com/compatibility-score.html?dependency-name=oj&package-manager=bundler&version-scheme=semver)
7
+ [![TideLift](https://tidelift.com/badges/github/ohler55/oj)](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 = rb_str_new(str, len);
26
+ volatile VALUE rstr = oj_cstr_to_value(str, len, (size_t)pi->options.cache_str);
27
27
 
28
28
  if (Qundef == rkey) {
29
- rkey = rb_str_new(key, klen);
30
- rstr = oj_encode(rstr);
31
- rkey = oj_encode(rkey);
32
- if (Yes == pi->options.sym_key) {
33
- rkey = rb_str_intern(rkey);
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 = rb_str_new(str, len);
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
- calc_hash_key(pi, parent),
151
+ oj_calc_hash_key(pi, parent),
146
152
  rval);
147
153
  } else {
148
- rb_hash_aset(stack_peek(&pi->stack)->val, calc_hash_key(pi, parent), rval);
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
- calc_hash_key(pi, parent),
170
+ oj_calc_hash_key(pi, parent),
165
171
  value);
166
172
  } else {
167
- rb_hash_aset(stack_peek(&pi->stack)->val, calc_hash_key(pi, parent), value);
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 = rb_str_new(str, len);
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, calc_hash_key(pi, kval), rval);
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, calc_hash_key(pi, kval), value); break;
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 (COL_VAL == leaf->value_type && 0 != leaf->elements) {
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 = 0;
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?() => String
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?", doc_where, 0);
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 HASH_MASK 0x000003FF
9
- #define HASH_SLOT_CNT 1024
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
- int i;
104
- KeyVal b;
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
- if (Qundef == state_class) {
393
- oj_define_mimic_json(0, NULL, Qnil);
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
- oj_define_mimic_json(0, NULL, Qnil);
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
- rkey = rb_funcall(rkey, oj_to_sym_id, 0);
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_|Array] either nil or an Array of classes to ignore when dumping
283
- * - *:ignore_under* [Boolean] if true then attributes that start with _ are ignored when dumping in
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
- // oj_default_options.mode = RailsMode;
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
- calc_hash_key(pi, kval),
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
- calc_hash_key(pi, kval),
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
- calc_hash_key(pi, kval),
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 = rb_str_new(str, len);
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 = rb_str_new(str, len);
123
+ volatile VALUE rstr = oj_cstr_to_value(str, len, (size_t)pi->options.cache_str);
83
124
 
84
- rstr = oj_encode(rstr);
85
- rb_hash_aset(stack_peek(&pi->stack)->val, calc_hash_key(pi, parent), rstr);
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, calc_hash_key(pi, parent), v);
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, calc_hash_key(pi, parent), value);
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 = rb_str_new(str, len);
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 oj_encode(v);
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
@@ -23,6 +23,8 @@ module Oj
23
23
  bigdecimal_load: :auto,
24
24
  circular: false,
25
25
  class_cache: false,
26
+ cache_keys: true,
27
+ cache_str: false,
26
28
  create_additions: false,
27
29
  create_id: "json_class",
28
30
  empty_string: false,
data/lib/oj/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
2
  module Oj
3
3
  # Current version of the module.
4
- VERSION = '3.11.5'
4
+ VERSION = '3.12.1'
5
5
  end
data/test/foo.rb CHANGED
@@ -8,48 +8,13 @@ end
8
8
 
9
9
  require 'oj'
10
10
 
11
- class Foo
12
- def initialize
13
- @x = 123
14
- end
11
+ Oj.default_options = {cache_keys: false, cache_str: 0}
15
12
 
16
- def xto_json(opt=nil, options=nil)
17
- "---to_json---"
18
- end
19
- end
20
-
21
- class Bar < Foo
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
- m = bar.method('to_json')
55
- puts "*** method: #{m} owner: #{m.owner} params: #{m.parameters}"
20
+ # check saj and scp
data/test/perf.rb CHANGED
@@ -17,7 +17,7 @@ class Perf
17
17
  end
18
18
  end
19
19
  end
20
-
20
+
21
21
  def run(iter)
22
22
  base = Item.new(nil, nil) { }
23
23
  base.run(iter, 0.0)
data/test/perf_scp.rb CHANGED
@@ -14,16 +14,16 @@ require 'oj'
14
14
 
15
15
  $verbose = false
16
16
  $indent = 0
17
- $iter = 50000
17
+ $iter = 50_000
18
18
  $with_bignum = false
19
- $size = 0
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)") { |i| $size = i }
26
- opts.on("-b", "with bignum") { $with_bignum = true }
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 => :compat }
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| Oj.strict_load(Oj.dump(o, :mode => :strict)) }
81
- capture_error('Yajl', $obj, 'encode', 'parse') { |o| require 'yajl'; Yajl::Parser.parse(Yajl::Encoder.encode(o)) }
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
- capture_error('JSON::Pure', $obj, 'generate', 'parse') { |o|
90
- require 'json/pure'
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, :mode => :strict) }
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: UTF-8
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
- assert_equal(val, doc.fetch(path))
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
@@ -123,6 +123,8 @@ class Juice < Minitest::Test
123
123
  compat_bigdecimal: true,
124
124
  create_id: 'classy',
125
125
  create_additions: true,
126
+ cache_keys: false,
127
+ cache_str: 5,
126
128
  space: 'z',
127
129
  array_nl: 'a',
128
130
  object_nl: 'o',
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.11.5
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-04-15 00:00:00.000000000 Z
11
+ date: 2021-07-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake-compiler