oj 3.11.4 → 3.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9a35082bc326dc1a19a23f38686fd6b9694a552ee01e753adffada1eb95e6256
4
- data.tar.gz: 5af5d53ba213df16a42c9da06e2ddbff730102f7ec65d6428e54ca1f4fa4c89e
3
+ metadata.gz: d5bf6799d9923c05b62b3bde88f052bb4373e9e0f15153f59e137b1023205a23
4
+ data.tar.gz: 872155b5f53adb9a8dfbd20e54a9b1a329762c353b25b656eba071e85966acbd
5
5
  SHA512:
6
- metadata.gz: 611c9c0684e42ad403e184ee825e0d7d6e4a10b45ca3be52d160cbc2fec02afaa59bf492c6b5f2f06c79baebd63eb81c660497f40fe9af45348db0304946c0f8
7
- data.tar.gz: 30b597c8eb046ea774565f6453950d1d2202daa969d9017f16616cb7ee906e44caca4b35a9b56e8a387572521fe7e02d32e8b38f718538b3655e316660cb65c7
6
+ metadata.gz: 5426970bd5c337bb387de75baa6589a82e81549d54b77c4ee7557fc52767cc933dcafb88b30e995ddf0c265ccd3e0d572c173bd4246d7c3998894879808aa68e
7
+ data.tar.gz: d6748332981816586e67aa48b80f0730b7bfe13b124e0afea70fae323b8908b49dede05b39f80586a017391dc7d257ad889a2f828cedfdd0ccd272835f69e2a6
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,26 @@ 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);
29
+ VALUE *slot;
30
+
32
31
  if (Yes == pi->options.sym_key) {
33
- rkey = rb_str_intern(rkey);
32
+ if (Qnil == (rkey = oj_sym_hash_get(key, klen, &slot))) {
33
+ rkey = rb_str_new(key, klen);
34
+ rkey = oj_encode(rkey);
35
+ rkey = rb_str_intern(rkey);
36
+ *slot = rkey;
37
+ rb_gc_register_address(slot);
38
+ }
39
+ } else {
40
+ if (Qnil == (rkey = oj_str_hash_get(key, klen, &slot))) {
41
+ rkey = rb_str_new(key, klen);
42
+ rkey = oj_encode(rkey);
43
+ *slot = rkey;
44
+ rb_gc_register_address(slot);
45
+ }
34
46
  }
35
47
  }
36
48
  if (Yes == pi->options.create_ok && NULL != pi->options.str_rx.head) {
@@ -93,23 +105,9 @@ static void end_hash(struct _parseInfo *pi) {
93
105
  }
94
106
  }
95
107
 
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
108
  static void add_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
110
- volatile VALUE rstr = rb_str_new(str, len);
109
+ volatile VALUE rstr = oj_cstr_to_value(str, len, (size_t)pi->options.cache_str);
111
110
 
112
- rstr = oj_encode(rstr);
113
111
  if (Yes == pi->options.create_ok && NULL != pi->options.str_rx.head) {
114
112
  VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, (int)len);
115
113
 
@@ -142,10 +140,10 @@ static void hash_set_num(struct _parseInfo *pi, Val parent, NumInfo ni) {
142
140
  rb_funcall(stack_peek(&pi->stack)->val,
143
141
  rb_intern("[]="),
144
142
  2,
145
- calc_hash_key(pi, parent),
143
+ oj_calc_hash_key(pi, parent),
146
144
  rval);
147
145
  } else {
148
- rb_hash_aset(stack_peek(&pi->stack)->val, calc_hash_key(pi, parent), rval);
146
+ rb_hash_aset(stack_peek(&pi->stack)->val, oj_calc_hash_key(pi, parent), rval);
149
147
  }
150
148
  if (Yes == pi->options.trace) {
151
149
  oj_trace_parse_call("set_number", pi, __FILE__, __LINE__, rval);
@@ -161,10 +159,10 @@ static void hash_set_value(ParseInfo pi, Val parent, VALUE value) {
161
159
  rb_funcall(stack_peek(&pi->stack)->val,
162
160
  rb_intern("[]="),
163
161
  2,
164
- calc_hash_key(pi, parent),
162
+ oj_calc_hash_key(pi, parent),
165
163
  value);
166
164
  } else {
167
- rb_hash_aset(stack_peek(&pi->stack)->val, calc_hash_key(pi, parent), value);
165
+ rb_hash_aset(stack_peek(&pi->stack)->val, oj_calc_hash_key(pi, parent), value);
168
166
  }
169
167
  if (Yes == pi->options.trace) {
170
168
  oj_trace_parse_call("set_value", pi, __FILE__, __LINE__, value);
@@ -199,9 +197,8 @@ static void array_append_num(ParseInfo pi, NumInfo ni) {
199
197
  }
200
198
 
201
199
  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);
200
+ volatile VALUE rstr = oj_cstr_to_value(str, len, (size_t)pi->options.cache_str);
203
201
 
204
- rstr = oj_encode(rstr);
205
202
  if (Yes == pi->options.create_ok && NULL != pi->options.str_rx.head) {
206
203
  VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, (int)len);
207
204
 
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
@@ -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
 
@@ -117,7 +121,18 @@ oj_class_hash_get(const char *key, size_t len, VALUE **slotp) {
117
121
  return hash_get(&class_hash, key, len, slotp, Qnil);
118
122
  }
119
123
 
120
- ID oj_attr_hash_get(const char *key, size_t len, ID **slotp) {
124
+ VALUE
125
+ oj_str_hash_get(const char *key, size_t len, VALUE **slotp) {
126
+ return hash_get(&str_hash, key, len, slotp, Qnil);
127
+ }
128
+
129
+ VALUE
130
+ oj_sym_hash_get(const char *key, size_t len, VALUE **slotp) {
131
+ return hash_get(&sym_hash, key, len, slotp, Qnil);
132
+ }
133
+
134
+ ID
135
+ oj_attr_hash_get(const char *key, size_t len, ID **slotp) {
121
136
  return (ID)hash_get(&intern_hash, key, len, (VALUE **)slotp, 0);
122
137
  }
123
138
 
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
@@ -19,7 +19,7 @@ VALUE oj_object_nl_sym;
19
19
  VALUE oj_space_before_sym;
20
20
  VALUE oj_space_sym;
21
21
 
22
- static VALUE state_class;
22
+ static VALUE state_class = Qundef;
23
23
 
24
24
  // mimic JSON documentation
25
25
 
@@ -389,6 +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
395
  active_hack[0] = rb_funcall(state_class, oj_new_id, 0);
393
396
  oj_dump_obj_to_json_using_params(*argv, copts, &out, 1, active_hack);
394
397
  }
@@ -476,6 +479,9 @@ oj_mimic_pretty_generate(int argc, VALUE *argv, VALUE self) {
476
479
  if (Qfalse == rb_funcall(h, oj_has_key_id, 1, oj_array_nl_sym)) {
477
480
  rb_hash_aset(h, oj_array_nl_sym, rb_str_new2("\n"));
478
481
  }
482
+ if (Qundef == state_class) {
483
+ oj_define_mimic_json(0, NULL, Qnil);
484
+ }
479
485
  rargs[1] = rb_funcall(state_class, oj_new_id, 1, h);
480
486
 
481
487
  copts.str_rx.head = NULL;
@@ -707,6 +713,8 @@ static struct _options mimic_object_to_json_options = {0, // indent
707
713
  No, // safe
708
714
  false, // sec_prec_set
709
715
  No, // ignore_under
716
+ Yes, // cache_keys
717
+ 3, // cache_str
710
718
  0, // int_range_min
711
719
  0, // int_range_max
712
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
 
@@ -1610,7 +1647,8 @@ extern VALUE oj_define_mimic_json(int argc, VALUE *argv, VALUE self);
1610
1647
  *
1611
1648
  * Encode obj as a JSON String. The obj argument must be a Hash, Array, or
1612
1649
  * respond to to_h or to_json. Options other than those listed such as
1613
- * +:allow_nan+ or +:max_nesting+ are ignored.
1650
+ * +:allow_nan+ or +:max_nesting+ are ignored. Calling this method will call
1651
+ * Oj.mimic_JSON if it is not already called.
1614
1652
  *
1615
1653
  * - *obj* [_Object__|_Hash_|_Array_] object to convert to a JSON String
1616
1654
  * - *opts* [_Hash_] options
@@ -1815,6 +1853,10 @@ void Init_oj() {
1815
1853
  rb_gc_register_address(&bigdecimal_load_sym);
1816
1854
  bigdecimal_sym = ID2SYM(rb_intern("bigdecimal"));
1817
1855
  rb_gc_register_address(&bigdecimal_sym);
1856
+ cache_keys_sym = ID2SYM(rb_intern("cache_keys"));
1857
+ rb_gc_register_address(&cache_keys_sym);
1858
+ cache_str_sym = ID2SYM(rb_intern("cache_str"));
1859
+ rb_gc_register_address(&cache_str_sym);
1818
1860
  circular_sym = ID2SYM(rb_intern("circular"));
1819
1861
  rb_gc_register_address(&circular_sym);
1820
1862
  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,27 @@ void oj_dump_wab_val(VALUE obj, int depth, Out out) {
292
293
 
293
294
  ///// load functions /////
294
295
 
296
+ static VALUE oj_hash_key(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
+ VALUE *slot;
306
+
307
+ if (Qnil == (rkey = oj_sym_hash_get(parent->key, parent->klen, &slot))) {
308
+ rkey = rb_str_new(parent->key, parent->klen);
309
+ rkey = oj_encode(rkey);
310
+ rkey = rb_str_intern(rkey);
311
+ *slot = rkey;
312
+ rb_gc_register_address(slot);
313
+ }
314
+ return rkey;
315
+ }
316
+
295
317
  static void hash_end(ParseInfo pi) {
296
318
  if (Yes == pi->options.trace) {
297
319
  oj_trace_parse_hash_end(pi, __FILE__, __LINE__);
@@ -432,7 +454,7 @@ static VALUE protect_uri(VALUE rstr) {
432
454
  return rb_funcall(resolve_uri_class(), oj_parse_id, 1, rstr);
433
455
  }
434
456
 
435
- static VALUE cstr_to_rstr(const char *str, size_t len) {
457
+ static VALUE cstr_to_rstr(ParseInfo pi, const char *str, size_t len) {
436
458
  volatile VALUE v = Qnil;
437
459
 
438
460
  if (30 == len && '-' == str[4] && '-' == str[7] && 'T' == str[10] && ':' == str[13] &&
@@ -445,20 +467,20 @@ static VALUE cstr_to_rstr(const char *str, size_t len) {
445
467
  uuid_check(str, (int)len) && Qnil != resolve_wab_uuid_class()) {
446
468
  return rb_funcall(wab_uuid_clas, oj_new_id, 1, rb_str_new(str, len));
447
469
  }
448
- v = rb_str_new(str, len);
449
470
  if (7 < len && 0 == strncasecmp("http://", str, 7)) {
450
471
  int err = 0;
472
+ v = rb_str_new(str, len);
451
473
  volatile VALUE uri = rb_protect(protect_uri, v, &err);
452
474
 
453
475
  if (0 == err) {
454
476
  return uri;
455
477
  }
456
478
  }
457
- return oj_encode(v);
479
+ return oj_cstr_to_value(str, len, (size_t)pi->options.cache_str);
458
480
  }
459
481
 
460
482
  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);
483
+ pi->stack.head->val = cstr_to_rstr(pi, str, len);
462
484
  if (Yes == pi->options.trace) {
463
485
  oj_trace_parse_call("add_string", pi, __FILE__, __LINE__, pi->stack.head->val);
464
486
  }
@@ -484,22 +506,10 @@ static VALUE start_hash(ParseInfo pi) {
484
506
  return rb_hash_new();
485
507
  }
486
508
 
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
509
  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);
510
+ volatile VALUE rval = cstr_to_rstr(pi, str, len);
501
511
 
502
- rb_hash_aset(stack_peek(&pi->stack)->val, calc_hash_key(pi, parent), rval);
512
+ rb_hash_aset(stack_peek(&pi->stack)->val, oj_hash_key(parent), rval);
503
513
  if (Yes == pi->options.trace) {
504
514
  oj_trace_parse_call("set_string", pi, __FILE__, __LINE__, rval);
505
515
  }
@@ -512,14 +522,14 @@ static void hash_set_num(ParseInfo pi, Val parent, NumInfo ni) {
512
522
  oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value");
513
523
  }
514
524
  rval = oj_num_as_value(ni);
515
- rb_hash_aset(stack_peek(&pi->stack)->val, calc_hash_key(pi, parent), rval);
525
+ rb_hash_aset(stack_peek(&pi->stack)->val, oj_hash_key(parent), rval);
516
526
  if (Yes == pi->options.trace) {
517
527
  oj_trace_parse_call("set_number", pi, __FILE__, __LINE__, rval);
518
528
  }
519
529
  }
520
530
 
521
531
  static void hash_set_value(ParseInfo pi, Val parent, VALUE value) {
522
- rb_hash_aset(stack_peek(&pi->stack)->val, calc_hash_key(pi, parent), value);
532
+ rb_hash_aset(stack_peek(&pi->stack)->val, oj_hash_key(parent), value);
523
533
  if (Yes == pi->options.trace) {
524
534
  oj_trace_parse_call("set_value", pi, __FILE__, __LINE__, value);
525
535
  }
@@ -533,7 +543,7 @@ static VALUE start_array(ParseInfo pi) {
533
543
  }
534
544
 
535
545
  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);
546
+ volatile VALUE rval = cstr_to_rstr(pi, str, len);
537
547
 
538
548
  rb_ary_push(stack_peek(&pi->stack)->val, rval);
539
549
  if (Yes == pi->options.trace) {
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.4'
4
+ VERSION = '3.12.0'
5
5
  end
data/test/foo.rb CHANGED
@@ -7,49 +7,14 @@ $oj_dir = File.dirname(File.expand_path(File.dirname(__FILE__)))
7
7
  end
8
8
 
9
9
  require 'oj'
10
+ require 'active_support'
10
11
 
11
- class Foo
12
- def initialize
13
- @x = 123
14
- end
12
+ Oj.default_options = {trace: true}
15
13
 
16
- def xto_json(opt=nil, options=nil)
17
- "---to_json---"
18
- end
14
+ Oj::Rails.mimic_JSON
15
+ #Oj.mimic_JSON
16
+ begin
17
+ ::JSON.load('foo=&bar')
18
+ rescue Exception => e
19
+ puts "*** #{e.class}: #{e.message}"
19
20
  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}"
53
-
54
- m = bar.method('to_json')
55
- puts "*** method: #{m} owner: #{m.owner} params: #{m.parameters}"
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')
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ $: << File.dirname(__FILE__)
5
+ $oj_dir = File.dirname(File.expand_path(File.dirname(__FILE__)))
6
+ %w(lib ext).each do |dir|
7
+ $: << File.join($oj_dir, dir)
8
+ end
9
+
10
+ require 'minitest'
11
+ require 'minitest/autorun'
12
+ require 'oj'
13
+
14
+ class Generator < Minitest::Test
15
+
16
+ def test_before
17
+ json = Oj.generate({})
18
+ assert_equal("{}", json)
19
+ end
20
+
21
+ end
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.4
4
+ version: 3.12.0
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-14 00:00:00.000000000 Z
11
+ date: 2021-07-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake-compiler
@@ -240,6 +240,7 @@ files:
240
240
  - test/test_fast.rb
241
241
  - test/test_file.rb
242
242
  - test/test_gc.rb
243
+ - test/test_generate.rb
243
244
  - test/test_hash.rb
244
245
  - test/test_integer_range.rb
245
246
  - test/test_null.rb
@@ -364,6 +365,7 @@ test_files:
364
365
  - test/test_fast.rb
365
366
  - test/test_file.rb
366
367
  - test/test_gc.rb
368
+ - test/test_generate.rb
367
369
  - test/test_hash.rb
368
370
  - test/test_integer_range.rb
369
371
  - test/test_null.rb