oj 3.13.4 → 3.13.6

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: de6407e200d1233d9d36804f49cbd3827d6d318220c44221968dae032e80cc31
4
- data.tar.gz: 9b0c2f83be6a37ba83677a956e40ba2571128d5ae95efca8e24c56dfcb9fbfc9
3
+ metadata.gz: c01eed4e3f068cda64af85aa6fe2aa15d7c559a3d1947385a8122112ae1b80b4
4
+ data.tar.gz: caba410a8bfee592344b8cfb281da48932cc2871ee3cc7fe71dadefacb55dc13
5
5
  SHA512:
6
- metadata.gz: 506be0671f0aecc01447f625e54eaf3a7e70d6a0d148771e94a8968c18224dae2b9e6c06cd0321dc41f1874e0663dae2c20e43b5e3e88a04d2bb23f160d93a27
7
- data.tar.gz: ab6b025efb11d2c5181a869dfa52755a4c891bc0625a9746a8f3b0189792b5e550829f602eb26c84b125b84657a7a4445f3e2b878a01334c26133a0b910edbab
6
+ metadata.gz: 15dc55ed4572c0dcba2a3345aee97e5ab17f3106eef44c3d0a3c9e258915c1d95cae3bda8bbce6f21d3de08c22662a07ce7e39a52b77ff6c1f660cb576054e1f
7
+ data.tar.gz: 768977fae9b1c60baa679e32eb3d66d02206eb4357c2f6c29189b1d2dd27982a59f65648f8ee11424211f4e80a2a4ca11f19b285bee18db1d08ddbaba10fa1ca
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 3.13.6 - 2021-09-11
4
+
5
+ - Fixed unicode UTF 8 parsing in string values.
6
+
7
+ - Fixed hash key allocation issue.
8
+
9
+ - The `Oj::Parser.new()` function now allows optional arguments that
10
+ set the allowed options for the mode. As an example
11
+ `Oj::Parser.new(:usual, cache_keys: true)`.
12
+
13
+ ## 3.13.5 - 2021-09-08
14
+
15
+ - Assure value strings of zero length are not always cached.
16
+
3
17
  ## 3.13.4 - 2021-09-04
4
18
 
5
19
  - Fixed concurrent GC issue in the cache.
data/README.md CHANGED
@@ -64,7 +64,7 @@ links.
64
64
 
65
65
  ## Releases
66
66
 
67
- See [{file:CHANGELOG.md}](CHANGELOG.md)
67
+ See [{file:CHANGELOG.md}](CHANGELOG.md) and [{file:RELEASE_NOTES.md}](RELEASE_NOTES.md)
68
68
 
69
69
  ## Links
70
70
 
data/RELEASE_NOTES.md ADDED
@@ -0,0 +1,55 @@
1
+ # RELEASE NOTES
2
+
3
+ The release notes here are organized by release. For a list of changes
4
+ see the See [{file:CHANGELOG.md}](CHANGELOG.md) file. In this file are
5
+ the steps to take to aid in keeping things rolling after updating to
6
+ the latest version.
7
+
8
+ ## 3.13.x
9
+
10
+ This release included a new cache that performs better than the
11
+ earlier cache and a new high performance parser.
12
+
13
+ ### Cache
14
+
15
+ The new cache includes a least recently used expiration to reduce
16
+ memory use. The cache is also self adjusting and will expand as needed
17
+ for better performance. It also handles Hash keys and string values
18
+ with two options, `:cache_keys`, a boolean and `:cache_str` an
19
+ integer. The `:cache_str` if set to more than zero is the limit for
20
+ the length of string values to cache. The maximum value is 35 which
21
+ allows strings up to 34 bytes to be cached.
22
+
23
+ One interesting aspect of the cache is not so much the string caching
24
+ which performs similar to the Ruby intern functions but the caching of
25
+ symbols and object attribute names. There is a significant gain for
26
+ symbols and object attributes.
27
+
28
+ If the cache is not desired then setting the default options to turn
29
+ it off can be done with this line:
30
+
31
+ ``` ruby
32
+ Oj.default_options = { cache_keys: false, cache_str: 0 }
33
+ ```
34
+
35
+ ### Oj::Parser
36
+
37
+ The new parser uses a different core that follows the approach taken
38
+ by [OjC](https://github.com/ohler55/ojc) and
39
+ [OjG](https://github.com/ohler55/ojg). It also takes advantage of the
40
+ bulk Array and Hash functions. Another issue the new parser addresses
41
+ is option management. Instead of a single global default_options each
42
+ parser instance maintains it's own options.
43
+
44
+ There is a price to be paid when using the Oj::Parser. The API is not
45
+ the same the older parser. A single parser can only be used in a
46
+ single thread. This allows reuse of internal buffers for additional
47
+ improvements in performance.
48
+
49
+ The performane advantage of the Oj::Parse is that it is more than 3
50
+ times faster than the Oj::compat_load call and 6 times faster than the
51
+ JSON gem.
52
+
53
+ ### Dump Performance
54
+
55
+ Thanks to Watson1978 Oj.dump also received a speed boost.
data/ext/oj/custom.c CHANGED
@@ -955,8 +955,8 @@ 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);
959
- volatile VALUE rstr = rb_utf8_str_new(str, len);
958
+ volatile VALUE rstr = oj_cstr_to_value(str, len, (size_t)pi->options.cache_str);
959
+ //volatile VALUE rstr = rb_utf8_str_new(str, len);
960
960
 
961
961
  if (Qundef == rkey) {
962
962
  if (Yes == pi->options.sym_key) {
data/ext/oj/parser.c CHANGED
@@ -610,6 +610,9 @@ static void parse(ojParser p, const byte *json) {
610
610
  printf("*** parse - mode: %c %s\n", p->map[256], (const char *)json);
611
611
  #endif
612
612
  for (; '\0' != *b; b++) {
613
+ #if DEBUG
614
+ printf("*** parse - mode: %c %02x %s => %c\n", p->map[256], *b, b, p->map[*b]);
615
+ #endif
613
616
  switch (p->map[*b]) {
614
617
  case SKIP_NEWLINE:
615
618
  p->line++;
@@ -887,13 +890,17 @@ static void parse(ojParser p, const byte *json) {
887
890
  buf_append_string(&p->buf, (const char *)start, b - start);
888
891
  }
889
892
  if ('"' == *b) {
893
+ p->funcs[p->stack[p->depth]].add_str(p);
890
894
  p->map = p->next_map;
891
895
  break;
892
896
  }
893
897
  b--;
894
898
  break;
895
899
  case STR_SLASH: p->map = esc_map; break;
896
- case STR_QUOTE: p->map = p->next_map; break;
900
+ case STR_QUOTE:
901
+ p->funcs[p->stack[p->depth]].add_str(p);
902
+ p->map = p->next_map;
903
+ break;
897
904
  case ESC_U:
898
905
  p->map = u_map;
899
906
  p->ri = 0;
@@ -1135,13 +1142,42 @@ extern void oj_set_parser_saj(ojParser p);
1135
1142
  extern void oj_set_parser_usual(ojParser p);
1136
1143
  extern void oj_set_parser_debug(ojParser p);
1137
1144
 
1145
+ static int opt_cb(VALUE rkey, VALUE value, VALUE ptr) {
1146
+ ojParser p = (ojParser)ptr;
1147
+ const char *key = NULL;
1148
+ char set_key[64];
1149
+ long klen;
1150
+
1151
+ switch (rb_type(rkey)) {
1152
+ case RUBY_T_SYMBOL:
1153
+ rkey = rb_sym2str(rkey);
1154
+ // fall through
1155
+ case RUBY_T_STRING:
1156
+ key = rb_string_value_ptr(&rkey);
1157
+ klen = RSTRING_LEN(rkey);
1158
+ break;
1159
+ default: rb_raise(rb_eArgError, "option keys must be a symbol or string");
1160
+ }
1161
+ if ((long)sizeof(set_key) - 1 <= klen) {
1162
+ return ST_CONTINUE;
1163
+ }
1164
+ memcpy(set_key, key, klen);
1165
+ set_key[klen] = '=';
1166
+ set_key[klen + 1] = '\0';
1167
+ p->option(p, set_key, value);
1168
+
1169
+ return ST_CONTINUE;
1170
+ }
1171
+
1138
1172
  /* Document-method: new
1139
1173
  * call-seq: new(mode=nil)
1140
1174
  *
1141
1175
  * Creates a new Parser with the specified mode. If no mode is provided
1142
- * validation is assumed.
1176
+ * validation is assumed. Optional arguments can be provided that match the
1177
+ * mode. For example with the :usual mode the call might look like
1178
+ * Oj::Parser.new(:usual, cache_keys: true).
1143
1179
  */
1144
- static VALUE parser_new(VALUE self, VALUE mode) {
1180
+ static VALUE parser_new(int argc, VALUE *argv, VALUE self) {
1145
1181
  ojParser p = ALLOC(struct _ojParser);
1146
1182
 
1147
1183
  #if HAVE_RB_EXT_RACTOR_SAFE
@@ -1151,33 +1187,45 @@ static VALUE parser_new(VALUE self, VALUE mode) {
1151
1187
  memset(p, 0, sizeof(struct _ojParser));
1152
1188
  buf_init(&p->key);
1153
1189
  buf_init(&p->buf);
1154
-
1155
1190
  p->map = value_map;
1156
- if (Qnil == mode) {
1157
- oj_set_parser_validator(p);
1191
+
1192
+ if (argc < 1) {
1193
+ oj_set_parser_validator(p);
1158
1194
  } else {
1159
- const char *ms = NULL;
1160
-
1161
- switch (rb_type(mode)) {
1162
- case RUBY_T_SYMBOL:
1163
- mode = rb_sym2str(mode);
1164
- // fall through
1165
- case RUBY_T_STRING: ms = RSTRING_PTR(mode); break;
1166
- default: rb_raise(rb_eArgError, "mode must be :validate, :usual, :saj, or :object");
1167
- }
1168
- if (0 == strcmp("usual", ms) || 0 == strcmp("standard", ms) || 0 == strcmp("strict", ms) ||
1169
- 0 == strcmp("compat", ms)) {
1170
- oj_set_parser_usual(p);
1171
- } else if (0 == strcmp("object", ms)) {
1172
- // TBD
1173
- } else if (0 == strcmp("saj", ms)) {
1174
- oj_set_parser_saj(p);
1175
- } else if (0 == strcmp("validate", ms)) {
1195
+ VALUE mode = argv[0];
1196
+
1197
+ if (Qnil == mode) {
1176
1198
  oj_set_parser_validator(p);
1177
- } else if (0 == strcmp("debug", ms)) {
1178
- oj_set_parser_debug(p);
1179
1199
  } else {
1180
- rb_raise(rb_eArgError, "mode must be :validate, :usual, :saj, or :object");
1200
+ const char *ms = NULL;
1201
+
1202
+ switch (rb_type(mode)) {
1203
+ case RUBY_T_SYMBOL:
1204
+ mode = rb_sym2str(mode);
1205
+ // fall through
1206
+ case RUBY_T_STRING: ms = RSTRING_PTR(mode); break;
1207
+ default: rb_raise(rb_eArgError, "mode must be :validate, :usual, :saj, or :object");
1208
+ }
1209
+ if (0 == strcmp("usual", ms) || 0 == strcmp("standard", ms) || 0 == strcmp("strict", ms) ||
1210
+ 0 == strcmp("compat", ms)) {
1211
+ oj_set_parser_usual(p);
1212
+ } else if (0 == strcmp("object", ms)) {
1213
+ // TBD
1214
+ } else if (0 == strcmp("saj", ms)) {
1215
+ oj_set_parser_saj(p);
1216
+ } else if (0 == strcmp("validate", ms)) {
1217
+ oj_set_parser_validator(p);
1218
+ } else if (0 == strcmp("debug", ms)) {
1219
+ oj_set_parser_debug(p);
1220
+ } else {
1221
+ rb_raise(rb_eArgError, "mode must be :validate, :usual, :saj, or :object");
1222
+ }
1223
+ }
1224
+ if (1 < argc) {
1225
+ VALUE ropts = argv[1];
1226
+
1227
+ Check_Type(ropts, T_HASH);
1228
+ rb_hash_foreach(ropts, opt_cb, (VALUE)p);
1181
1229
  }
1182
1230
  }
1183
1231
  return Data_Wrap_Struct(parser_class, parser_mark, parser_free, p);
@@ -1204,7 +1252,7 @@ static VALUE parser_new(VALUE self, VALUE mode) {
1204
1252
  * - *:usual*
1205
1253
  * - _cache_keys=_ sets the value of the _cache_keys_ flag.
1206
1254
  * - _cache_keys_ returns the value of the _cache_keys_ flag.
1207
- * - _cache_strings=_ sets the value of the _cache_strings_ to an positive integer less than 35. Strings shorter than
1255
+ * - _cache_strings=_ sets the value of the _cache_strings_ to a positive integer less than 35. Strings shorter than
1208
1256
  * that length are cached.
1209
1257
  * - _cache_strings_ returns the value of the _cache_strings_ integer value.
1210
1258
  * - _cache_expunge=_ sets the value of the _cache_expunge_ where 0 never expunges, 1 expunges slowly, 2 expunges
@@ -1469,7 +1517,7 @@ static VALUE parser_validate(VALUE self) {
1469
1517
  */
1470
1518
  void oj_parser_init() {
1471
1519
  parser_class = rb_define_class_under(Oj, "Parser", rb_cObject);
1472
- rb_define_module_function(parser_class, "new", parser_new, 1);
1520
+ rb_define_module_function(parser_class, "new", parser_new, -1);
1473
1521
  rb_define_method(parser_class, "parse", parser_parse, 1);
1474
1522
  rb_define_method(parser_class, "load", parser_load, 1);
1475
1523
  rb_define_method(parser_class, "file", parser_file, 1);
data/ext/oj/strict.c CHANGED
@@ -16,7 +16,7 @@
16
16
  VALUE oj_cstr_to_value(const char *str, size_t len, size_t cache_str) {
17
17
  volatile VALUE rstr = Qnil;
18
18
 
19
- if (len <= cache_str) {
19
+ if (len < cache_str) {
20
20
  rstr = oj_str_intern(str, len);
21
21
  } else {
22
22
  rstr = rb_str_new(str, len);
@@ -37,7 +37,7 @@ VALUE oj_calc_hash_key(ParseInfo pi, Val parent) {
37
37
  } else {
38
38
  rkey = rb_str_new(parent->key, parent->klen);
39
39
  rkey = oj_encode(rkey);
40
- OBJ_FREEZE(rkey);
40
+ OBJ_FREEZE(rkey); // frozen when used as a Hash key anyway
41
41
  }
42
42
  return rkey;
43
43
  }
data/ext/oj/usual.c CHANGED
@@ -39,7 +39,7 @@ typedef struct _col {
39
39
  typedef union _key {
40
40
  struct {
41
41
  int16_t len;
42
- char buf[22];
42
+ char buf[30];
43
43
  };
44
44
  struct {
45
45
  int16_t xlen; // should be the same as len
@@ -209,21 +209,21 @@ static void push(ojParser p, VALUE v) {
209
209
  static VALUE cache_key(ojParser p, Key kp) {
210
210
  Delegate d = (Delegate)p->ctx;
211
211
 
212
- if ((size_t)kp->len < sizeof(kp->buf) - 1) {
212
+ if ((size_t)kp->len < sizeof(kp->buf)) {
213
213
  return cache_intern(d->key_cache, kp->buf, kp->len);
214
214
  }
215
215
  return cache_intern(d->key_cache, kp->key, kp->len);
216
216
  }
217
217
 
218
218
  static VALUE str_key(ojParser p, Key kp) {
219
- if ((size_t)kp->len < sizeof(kp->buf) - 1) {
219
+ if ((size_t)kp->len < sizeof(kp->buf)) {
220
220
  return rb_str_freeze(rb_utf8_str_new(kp->buf, kp->len));
221
221
  }
222
222
  return rb_str_freeze(rb_utf8_str_new(kp->key, kp->len));
223
223
  }
224
224
 
225
225
  static VALUE sym_key(ojParser p, Key kp) {
226
- if ((size_t)kp->len < sizeof(kp->buf) - 1) {
226
+ if ((size_t)kp->len < sizeof(kp->buf)) {
227
227
  return rb_str_freeze(rb_str_intern(rb_utf8_str_new(kp->buf, kp->len)));
228
228
  }
229
229
  return rb_str_freeze(rb_str_intern(rb_utf8_str_new(kp->key, kp->len)));
@@ -232,7 +232,7 @@ static VALUE sym_key(ojParser p, Key kp) {
232
232
  static ID get_attr_id(ojParser p, Key kp) {
233
233
  Delegate d = (Delegate)p->ctx;
234
234
 
235
- if ((size_t)kp->len < sizeof(kp->buf) - 1) {
235
+ if ((size_t)kp->len < sizeof(kp->buf)) {
236
236
  return (ID)cache_intern(d->attr_cache, kp->buf, kp->len);
237
237
  }
238
238
  return (ID)cache_intern(d->attr_cache, kp->key, kp->len);
@@ -253,7 +253,7 @@ static void push_key(ojParser p) {
253
253
  d->kend = d->khead + cap;
254
254
  }
255
255
  d->ktail->len = klen;
256
- if (klen <= sizeof(d->ktail->buf) + 1) {
256
+ if (klen < sizeof(d->ktail->buf)) {
257
257
  memcpy(d->ktail->buf, key, klen);
258
258
  d->ktail->buf[klen] = '\0';
259
259
  } else {
@@ -336,7 +336,7 @@ static void close_object(ojParser p) {
336
336
  #if HAVE_RB_HASH_BULK_INSERT
337
337
  for (vp = head; kp < d->ktail; kp++, vp += 2) {
338
338
  *vp = d->get_key(p, kp);
339
- if (sizeof(kp->buf) - 1 < (size_t)kp->len) {
339
+ if (sizeof(kp->buf) <= (size_t)kp->len) {
340
340
  xfree(kp->key);
341
341
  }
342
342
  }
@@ -344,7 +344,7 @@ static void close_object(ojParser p) {
344
344
  #else
345
345
  for (vp = head; kp < d->ktail; kp++, vp += 2) {
346
346
  rb_hash_aset(obj, d->get_key(p, kp), *(vp + 1));
347
- if (sizeof(kp->buf) - 1 < (size_t)kp->len) {
347
+ if (sizeof(kp->buf) <= (size_t)kp->len) {
348
348
  xfree(kp->key);
349
349
  }
350
350
  }
@@ -368,7 +368,7 @@ static void close_object_class(ojParser p) {
368
368
 
369
369
  for (vp = head; kp < d->ktail; kp++, vp += 2) {
370
370
  rb_funcall(obj, hset_id, 2, d->get_key(p, kp), *(vp + 1));
371
- if (sizeof(kp->buf) - 1 < (size_t)kp->len) {
371
+ if (sizeof(kp->buf) <= (size_t)kp->len) {
372
372
  xfree(kp->key);
373
373
  }
374
374
  }
@@ -396,7 +396,7 @@ static void close_object_create(ojParser p) {
396
396
  #if HAVE_RB_HASH_BULK_INSERT
397
397
  for (vp = head; kp < d->ktail; kp++, vp += 2) {
398
398
  *vp = d->get_key(p, kp);
399
- if (sizeof(kp->buf) - 1 < (size_t)kp->len) {
399
+ if (sizeof(kp->buf) <= (size_t)kp->len) {
400
400
  xfree(kp->key);
401
401
  }
402
402
  }
@@ -404,7 +404,7 @@ static void close_object_create(ojParser p) {
404
404
  #else
405
405
  for (vp = head; kp < d->ktail; kp++, vp += 2) {
406
406
  rb_hash_aset(obj, d->get_key(p, kp), *(vp + 1));
407
- if (sizeof(kp->buf) - 1 < (size_t)kp->len) {
407
+ if (sizeof(kp->buf) <= (size_t)kp->len) {
408
408
  xfree(kp->key);
409
409
  }
410
410
  }
@@ -413,7 +413,7 @@ static void close_object_create(ojParser p) {
413
413
  obj = rb_class_new_instance(0, NULL, d->hash_class);
414
414
  for (vp = head; kp < d->ktail; kp++, vp += 2) {
415
415
  rb_funcall(obj, hset_id, 2, d->get_key(p, kp), *(vp + 1));
416
- if (sizeof(kp->buf) - 1 < (size_t)kp->len) {
416
+ if (sizeof(kp->buf) <= (size_t)kp->len) {
417
417
  xfree(kp->key);
418
418
  }
419
419
  }
@@ -428,7 +428,7 @@ static void close_object_create(ojParser p) {
428
428
  #if HAVE_RB_HASH_BULK_INSERT
429
429
  for (vp = head; kp < d->ktail; kp++, vp += 2) {
430
430
  *vp = d->get_key(p, kp);
431
- if (sizeof(kp->buf) - 1 < (size_t)kp->len) {
431
+ if (sizeof(kp->buf) <= (size_t)kp->len) {
432
432
  xfree(kp->key);
433
433
  }
434
434
  }
@@ -436,7 +436,7 @@ static void close_object_create(ojParser p) {
436
436
  #else
437
437
  for (vp = head; kp < d->ktail; kp++, vp += 2) {
438
438
  rb_hash_aset(arg, d->get_key(p, kp), *(vp + 1));
439
- if (sizeof(kp->buf) - 1 < (size_t)kp->len) {
439
+ if (sizeof(kp->buf) <= (size_t)kp->len) {
440
440
  xfree(kp->key);
441
441
  }
442
442
  }
@@ -446,7 +446,7 @@ static void close_object_create(ojParser p) {
446
446
  obj = rb_class_new_instance(0, NULL, clas);
447
447
  for (vp = head; kp < d->ktail; kp++, vp += 2) {
448
448
  rb_ivar_set(obj, get_attr_id(p, kp), *(vp + 1));
449
- if (sizeof(kp->buf) - 1 < (size_t)kp->len) {
449
+ if (sizeof(kp->buf) <= (size_t)kp->len) {
450
450
  xfree(kp->key);
451
451
  }
452
452
  }
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.13.4'
4
+ VERSION = '3.13.6'
5
5
  end
data/test/bar.rb ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $: << '.'
4
+ $: << File.join(File.dirname(__FILE__), "../lib")
5
+ $: << File.join(File.dirname(__FILE__), "../ext")
6
+
7
+ require 'oj'
8
+
9
+ json = %|[{"x12345678901234567890": true}]|
10
+
11
+ p = Oj::Parser.new(:usual)
12
+ p.cache_keys = false
13
+ p.symbol_keys = true
14
+ x = p.parse(json)
15
+
16
+ pp x
data/test/baz.rb ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $: << File.dirname(__FILE__)
4
+ $oj_dir = File.dirname(File.expand_path(File.dirname(__FILE__)))
5
+ %w(lib ext).each do |dir|
6
+ $: << File.join($oj_dir, dir)
7
+ end
8
+
9
+ require 'oj'
10
+ Oj.mimic_JSON()
11
+
12
+ begin
13
+ ::JSON.load('name=&email=&subject=&comment=&submit=Send+Message')
14
+ rescue ::JSON::ParserError
15
+ puts "*** Pass"
16
+ end
data/test/foo.rb CHANGED
@@ -1,11 +1,13 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- $: << '.'
4
- $: << '../lib'
5
- $: << '../ext'
6
-
7
3
  require 'oj'
8
4
 
9
- p = Oj::Parser.new(:debug)
5
+ Oj::default_options = {cache_str: 0, cache_keys: true, mode: :strict}
6
+
7
+ puts "Ruby version: #{RUBY_VERSION}"
8
+ puts "Oj version: #{Oj::VERSION}"
9
+
10
+ puts "cache_keys: #{Oj::default_options[:cache_keys]}"
11
+ puts "cache_str: #{Oj::default_options[:cache_str]}"
10
12
 
11
- p.parse("[true, false]")
13
+ Oj.load('{"":""}').each_pair {|k,v| puts "k.frozen?: #{k.frozen?}\nv.frozen?: #{v.frozen?}"}
data/test/prec.rb ADDED
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'oj'
4
+
5
+ extras = {"locationLng" => -97.14690769100295}
6
+
7
+ Oj.default_options = {float_precision: 17}
8
+
9
+ encoded = Oj.dump(extras)
10
+ puts encoded
11
+ puts Oj.load(encoded)
12
+
13
+ require "active_record"
14
+
15
+ Oj::Rails.set_encoder()
16
+ Oj::Rails.set_decoder()
17
+
18
+ Oj.default_options = {float_precision: 17}
19
+ # Using Oj rails encoder, gets the correct value: {"locationLng":-97.14690769100295}
20
+ encoded = ActiveSupport::JSON.encode(extras)
21
+ puts encoded
22
+ puts ActiveSupport::JSON.decode(encoded)
23
+ puts Oj.load(encoded)
@@ -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
 
@@ -73,9 +73,14 @@ class UsualTest < Minitest::Test
73
73
  assert_equal({a: true, b: false}, doc)
74
74
  end
75
75
 
76
- def test_capacity
76
+ def test_strings
77
77
  p = Oj::Parser.new(:usual)
78
- p.capacity = 1000
78
+ doc = p.parse('{"ぴ": "", "ぴ ": "x", "c": "ぴーたー", "d": " ぴーたー "}')
79
+ assert_equal({'ぴ' => '', 'ぴ ' => 'x', 'c' => 'ぴーたー', 'd' => ' ぴーたー '}, doc)
80
+ end
81
+
82
+ def test_capacity
83
+ p = Oj::Parser.new(:usual, capacity: 1000)
79
84
  assert_equal(4096, p.capacity)
80
85
  p.capacity = 5000
81
86
  assert_equal(5000, p.capacity)
@@ -181,8 +186,7 @@ class UsualTest < Minitest::Test
181
186
  end
182
187
 
183
188
  def test_missing_class
184
- p = Oj::Parser.new(:usual)
185
- p.create_id = '^'
189
+ p = Oj::Parser.new(:usual, create_id: '^')
186
190
  json = '{"a":true,"^":"Auto","b":false}'
187
191
  doc = p.parse(json)
188
192
  assert_equal(Hash, doc.class)
data/test/zoo.rb ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #require 'json'
4
+
5
+ $: << File.dirname(__FILE__)
6
+ require 'helper'
7
+ require 'oj'
8
+
9
+ Oj.mimic_JSON
10
+ puts "\u3074"
11
+
12
+ puts JSON.dump(["\u3074"])
13
+ puts JSON.generate(["\u3074"])
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.13.4
4
+ version: 3.13.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter Ohler
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-09-04 00:00:00.000000000 Z
11
+ date: 2021-09-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake-compiler
@@ -79,6 +79,9 @@ extensions:
79
79
  - ext/oj/extconf.rb
80
80
  extra_rdoc_files:
81
81
  - README.md
82
+ - LICENSE
83
+ - CHANGELOG.md
84
+ - RELEASE_NOTES.md
82
85
  - pages/Advanced.md
83
86
  - pages/Compatibility.md
84
87
  - pages/Custom.md
@@ -94,6 +97,7 @@ files:
94
97
  - CHANGELOG.md
95
98
  - LICENSE
96
99
  - README.md
100
+ - RELEASE_NOTES.md
97
101
  - ext/oj/buf.h
98
102
  - ext/oj/cache.c
99
103
  - ext/oj/cache.h
@@ -195,8 +199,8 @@ files:
195
199
  - test/activesupport6/test_common.rb
196
200
  - test/activesupport6/test_helper.rb
197
201
  - test/activesupport6/time_zone_test_helpers.rb
198
- - test/benny.rb
199
- - test/big.rb
202
+ - test/bar.rb
203
+ - test/baz.rb
200
204
  - test/files.rb
201
205
  - test/foo.rb
202
206
  - test/helper.rb
@@ -232,6 +236,7 @@ files:
232
236
  - test/perf_simple.rb
233
237
  - test/perf_strict.rb
234
238
  - test/perf_wab.rb
239
+ - test/prec.rb
235
240
  - test/sample.rb
236
241
  - test/sample/change.rb
237
242
  - test/sample/dir.rb
@@ -258,7 +263,6 @@ files:
258
263
  - test/test_null.rb
259
264
  - test/test_object.rb
260
265
  - test/test_parser.rb
261
- - test/test_parser_memory.rb
262
266
  - test/test_parser_saj.rb
263
267
  - test/test_parser_usual.rb
264
268
  - test/test_rails.rb
@@ -271,6 +275,7 @@ files:
271
275
  - test/tests.rb
272
276
  - test/tests_mimic.rb
273
277
  - test/tests_mimic_addition.rb
278
+ - test/zoo.rb
274
279
  homepage: http://www.ohler.com/oj
275
280
  licenses:
276
281
  - MIT
@@ -281,7 +286,7 @@ metadata:
281
286
  homepage_uri: http://www.ohler.com/oj/
282
287
  source_code_uri: https://github.com/ohler55/oj
283
288
  wiki_uri: https://github.com/ohler55/oj/wiki
284
- post_install_message:
289
+ post_install_message:
285
290
  rdoc_options:
286
291
  - "--title"
287
292
  - Oj
@@ -301,7 +306,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
301
306
  version: '0'
302
307
  requirements: []
303
308
  rubygems_version: 3.2.22
304
- signing_key:
309
+ signing_key:
305
310
  specification_version: 4
306
311
  summary: A fast JSON parser and serializer.
307
312
  test_files:
@@ -325,8 +330,8 @@ test_files:
325
330
  - test/activesupport6/test_common.rb
326
331
  - test/activesupport6/test_helper.rb
327
332
  - test/activesupport6/time_zone_test_helpers.rb
328
- - test/benny.rb
329
- - test/big.rb
333
+ - test/bar.rb
334
+ - test/baz.rb
330
335
  - test/files.rb
331
336
  - test/foo.rb
332
337
  - test/helper.rb
@@ -362,6 +367,7 @@ test_files:
362
367
  - test/perf_simple.rb
363
368
  - test/perf_strict.rb
364
369
  - test/perf_wab.rb
370
+ - test/prec.rb
365
371
  - test/sample/change.rb
366
372
  - test/sample/dir.rb
367
373
  - test/sample/doc.rb
@@ -388,7 +394,6 @@ test_files:
388
394
  - test/test_null.rb
389
395
  - test/test_object.rb
390
396
  - test/test_parser.rb
391
- - test/test_parser_memory.rb
392
397
  - test/test_parser_saj.rb
393
398
  - test/test_parser_usual.rb
394
399
  - test/test_rails.rb
@@ -401,3 +406,4 @@ test_files:
401
406
  - test/tests.rb
402
407
  - test/tests_mimic.rb
403
408
  - test/tests_mimic_addition.rb
409
+ - test/zoo.rb
data/test/benny.rb DELETED
@@ -1,50 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal
3
-
4
- require 'bundler/inline'
5
-
6
- gemfile do
7
- source 'https://rubygems.org'
8
-
9
- gem 'oj'
10
- gem 'benchmark-ips', require: 'benchmark/ips'
11
- end
12
-
13
- require 'json'
14
- require 'open-uri'
15
-
16
- CANADA_DATA_JSON = URI.parse('https://raw.githubusercontent.com/serde-rs/json-benchmark/master/data/canada.json').read
17
- CANADA_DATA = JSON.parse(CANADA_DATA_JSON)
18
-
19
- Benchmark.ips do |x|
20
- x.config(:time => 10, :warmup => 5)
21
-
22
- x.report("marshall Canada data with Oj") do
23
- Oj.dump(CANADA_DATA)
24
- end
25
-
26
- x.report("marshall Canada data with JSON") do
27
- JSON.dump(CANADA_DATA)
28
- end
29
-
30
- x.compare!
31
- end
32
-
33
- Oj.default_options = {
34
- mode: :strict,
35
- bigdecimal_load: :fast
36
- }
37
-
38
- Benchmark.ips do |x|
39
- x.config(:time => 10, :warmup => 5)
40
-
41
- x.report("unmarshall Canada data with Oj") do
42
- Oj.load(CANADA_DATA_JSON)
43
- end
44
-
45
- x.report("unmarshall Canada data with JSON") do
46
- JSON.parse(CANADA_DATA_JSON)
47
- end
48
-
49
- x.compare!
50
- end
data/test/big.rb DELETED
@@ -1,15 +0,0 @@
1
- #require 'active_support'
2
- #require 'active_support/core_ext'
3
- #require 'active_support/json'
4
- require 'oj'
5
-
6
- #Oj.optimize_rails
7
- Oj.mimic_JSON
8
-
9
- h = {:type=>:record, :name=>:group, :namespace=>"com.salsify.identity", :fields=>[{:name=>"id", :type=>{:name=>:salsify_uuid, :type=>:fixed, :namespace=>"com.salsify", :size=>38}}, {:name=>"type", :type=>"string", :default=>"groups"}, {:name=>"external_id", :type=>[:null, "string"], :default=>nil}, {:name=>"created_at", :type=>{:type=>"long", :logicalType=>"timestamp-micros"}}, {:name=>"updated_at", :type=>{:type=>"long", :logicalType=>"timestamp-micros"}}, {:name=>"name", :type=>"string"}, {:name=>"policy", :type=>[:null, {:type=>:record, :name=>:policy, :namespace=>"com.salsify.security", :fields=>[{:name=>"created_at", :type=>{:type=>"long", :logicalType=>"timestamp-micros"}}, {:name=>"updated_at", :type=>{:type=>"long", :logicalType=>"timestamp-micros"}}, {:name=>"id", :type=>"com.salsify.salsify_uuid"}, {:name=>"type", :type=>"string", :default=>"policies"}, {:name=>"external_id", :type=>[:null, "string"], :default=>nil}, {:name=>"name", :type=>"string"}, {:name=>"statements", :type=>{:type=>:array, :items=>{:type=>:record, :name=>:statement, :namespace=>"com.salsify.security", :fields=>[{:name=>"created_at", :type=>{:type=>"long", :logicalType=>"timestamp-micros"}}, {:name=>"updated_at", :type=>{:type=>"long", :logicalType=>"timestamp-micros"}}, {:name=>"id", :type=>"com.salsify.salsify_uuid"}, {:name=>"type", :type=>"string", :default=>"statements"}, {:name=>"external_id", :type=>[:null, "string"], :default=>nil}, {:name=>"action", :type=>{:name=>"__statement_action_enum", :type=>:enum, :namespace=>"com.salsify.security", :symbols=>[:manage, :read]}}, {:name=>"resource", :type=>{:name=>"__statement_resource_enum", :type=>:enum, :namespace=>"com.salsify.security", :symbols=>[:product, :digital_asset]}}, {:name=>"conditions", :type=>{:type=>:array, :items=>{:type=>:record, :name=>:condition, :namespace=>"com.salsify.security", :fields=>[{:name=>"created_at", :type=>{:type=>"long", :logicalType=>"timestamp-micros"}}, {:name=>"updated_at", :type=>{:type=>"long", :logicalType=>"timestamp-micros"}}, {:name=>"id", :type=>"com.salsify.salsify_uuid"}, {:name=>"type", :type=>"string", :default=>"conditions"}, {:name=>"external_id", :type=>[:null, "string"], :default=>nil}, {:name=>"operator", :type=>{:name=>"__condition_operator_enum", :type=>:enum, :namespace=>"com.salsify.security", :symbols=>[:equals]}}, {:name=>"attribute_type", :type=>{:name=>"__condition_attribute_type_enum", :type=>:enum, :namespace=>"com.salsify.security", :symbols=>[:resource]}}, {:name=>"value", :type=>"string"}, {:name=>"attribute", :type=>[:null, {:type=>:record, :name=>:reference, :namespace=>"com.salsify", :fields=>[{:name=>"id", :type=>"com.salsify.salsify_uuid"}, {:name=>"type", :type=>"string", :doc=>"snake_case, plural name for the resource type"}, {:name=>"external_id", :type=>[:null, "string"], :default=>nil}]}], :default=>nil}, {:name=>"broken", :type=>[:null, "boolean"], :default=>nil}]}}}]}}}]}], :default=>nil}]}
10
-
11
- #Oj.dump(h)
12
- puts JSON.pretty_generate(h)
13
- #puts JSON.fast_generate(h)
14
- #puts JSON.generate(h)
15
-
@@ -1,45 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # encoding: utf-8
3
-
4
- %w(lib ext test).each do |dir|
5
- $LOAD_PATH.unshift File.expand_path("../../#{dir}", __FILE__)
6
- end
7
-
8
- require 'oj'
9
-
10
- json =<<-EOF
11
- {
12
- "$id": "https://example.com/person.schema.json",
13
- "$schema": "https://json-schema.org/draft/2020-12/schema",
14
- "title": "Person",
15
- "type": "object",
16
- "properties": {
17
- "firstName": {
18
- "type": "string",
19
- "description": "The person's first name."
20
- },
21
- "lastName": {
22
- "type": "string",
23
- "description": "The person's last name."
24
- },
25
- "age": {
26
- "description": "Age in years which must be equal to or greater than zero.",
27
- "type": "integer",
28
- "minimum": 0
29
- }
30
- }
31
- }
32
- EOF
33
-
34
- #json = '{"abc": true}'
35
- #json = '[true,false]'
36
-
37
- 100_001.times do |i|
38
- p = Oj::Parser.new(:usual).parse(json)
39
-
40
- if i % 10_000 == 0
41
- GC.start
42
- rss = Integer(`ps -o rss= -p #{Process.pid}`) / 1024.0
43
- puts "#{i},#{rss} MB"
44
- end
45
- end