oj 3.11.8 → 3.12.0

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: 11978d74b0eadb223adb22a84e45374f96cfe8b7129ca802e36987129bf75187
4
- data.tar.gz: a6b6963003481173d338127337ac68c7d62630ee101e18676c7d5a71da7c8596
3
+ metadata.gz: d5bf6799d9923c05b62b3bde88f052bb4373e9e0f15153f59e137b1023205a23
4
+ data.tar.gz: 872155b5f53adb9a8dfbd20e54a9b1a329762c353b25b656eba071e85966acbd
5
5
  SHA512:
6
- metadata.gz: 93a515179416171a536d139f4e34a6b81b526800671bd123a91c721c083513c6187a45baba56d0280e8396e1fa0997bf96f9cf360b1917db994b79634cd86c4a
7
- data.tar.gz: b22e208d8d4b5676acc9d4fedf9411d4433f184268af411d43b5efd361610f0218d5a1ca9ececdc71a391eb4fd90df5e4657050e2fe3943ad5a397988ee5bd5d
6
+ metadata.gz: 5426970bd5c337bb387de75baa6589a82e81549d54b77c4ee7557fc52767cc933dcafb88b30e995ddf0c265ccd3e0d572c173bd4246d7c3998894879808aa68e
7
+ data.tar.gz: d6748332981816586e67aa48b80f0730b7bfe13b124e0afea70fae323b8908b49dede05b39f80586a017391dc7d257ad889a2f828cedfdd0ccd272835f69e2a6
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/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
@@ -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
 
@@ -1816,6 +1853,10 @@ void Init_oj() {
1816
1853
  rb_gc_register_address(&bigdecimal_load_sym);
1817
1854
  bigdecimal_sym = ID2SYM(rb_intern("bigdecimal"));
1818
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);
1819
1860
  circular_sym = ID2SYM(rb_intern("circular"));
1820
1861
  rb_gc_register_address(&circular_sym);
1821
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.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/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.8'
4
+ VERSION = '3.12.0'
5
5
  end
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_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.8
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-07-03 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