oj 1.2.9 → 1.2.10

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of oj might be problematic. Click here for more details.

data/README.md CHANGED
@@ -24,9 +24,17 @@ A fast JSON parser and Object marshaller as a Ruby gem.
24
24
 
25
25
  ## <a name="release">Release Notes</a>
26
26
 
27
- ### Release 1.2.9
27
+ ### Release 1.2.10
28
28
 
29
- - Changed the error message for invalid keys in compat mode to identify offending type.
29
+ - Added check for circular on loading of circular dumped JSON.
30
+
31
+ - Added support for direct serialization of BigDecimal, Rational, Date, and DateTime.
32
+
33
+ - Added json.rb to $" in mimic mode to avoid pulling in the real JSON by accident.
34
+
35
+ - Oj is now thread safe for all functions.
36
+
37
+ - The / (solidus) character is now placed in strings without being escaped.
30
38
 
31
39
  ## <a name="description">Description</a>
32
40
 
@@ -55,7 +63,10 @@ to_hash() is more flexible and produces more consistent output so it has a
55
63
  preference over the to_json() method. If neither the to_json() or to_hash()
56
64
  methods exist then the Oj internal Object variable encoding is used.
57
65
 
58
- Oj is compatible with Ruby 1.8.7, 1.9.2, 1.9.3, JRuby, RBX, and the latest 2.0dev.
66
+ Oj is compatible with Ruby 1.8.7, 1.9.2, 1.9.3, JRuby, RBX, and the latest
67
+ 2.0dev. Note that JRuby now disables support for extentions by default. JRuby
68
+ can be build with extensions enabled. Check the documenation for JRuby
69
+ installs in your environment.
59
70
 
60
71
  Oj is also compatible with Rails. Just make sure the Oj gem is installed and
61
72
  [multi_json](https://github.com/intridea/multi_json) will pick it up and use it.
@@ -352,13 +363,16 @@ excaping the first character so that it appears as `\u005e` or `\u003a` instead
352
363
  class. The sequence `{"^c":"Oj::Bag"}` is read as the Oj::Bag class.
353
364
 
354
365
  6. A `"^t"` JSON Object key indicates the value should be converted to a Ruby
355
- Time. The sequence `{"^t":1325775487.000000}` is read as Jan 5, 2012 at 23:58:07.
366
+ Time. The sequence `{"^t":1325775487.000000}` is read as Jan 5, 2012 at
367
+ 23:58:07.
356
368
 
357
- 87. A `"^o"` JSON Object key indicates the value should be converted to a Ruby
369
+ 7. A `"^o"` JSON Object key indicates the value should be converted to a Ruby
358
370
  Object. The first entry in the JSON Object must be a class with the `"^o"`
359
371
  key. After that each entry is treated as a variable of the Object where the
360
372
  key is the variable name without the preceeding `@`. An example is
361
- `{"^o":"Oj::Bag","x":58,"y":"marbles"}`.
373
+ `{"^o":"Oj::Bag","x":58,"y":"marbles"}`. `"^O"` is the same except that it is
374
+ for built in or odd classes that don't obey the normal Ruby rules. Examples
375
+ are Rational, Date, and DateTime.
362
376
 
363
377
  8. A `"^u"` JSON Object key indicates the value should be converted to a Ruby
364
378
  Struct. The first entry in the JSON Object must be a class with the `"^u"`
@@ -67,6 +67,7 @@ static void dump_false(Out out);
67
67
  static void dump_fixnum(VALUE obj, Out out);
68
68
  static void dump_bignum(VALUE obj, Out out);
69
69
  static void dump_float(VALUE obj, Out out);
70
+ static void dump_raw(const char *str, size_t cnt, Out out);
70
71
  static void dump_cstr(const char *str, size_t cnt, int is_sym, int escape1, Out out);
71
72
  static void dump_hex(u_char c, Out out);
72
73
  static void dump_str_comp(VALUE obj, Out out);
@@ -82,7 +83,7 @@ static int hash_cb_object(VALUE key, VALUE value, Out out);
82
83
  static void dump_hash(VALUE obj, int depth, int mode, Out out);
83
84
  static void dump_time(VALUE obj, Out out);
84
85
  static void dump_data_comp(VALUE obj, Out out);
85
- static void dump_data_obj(VALUE obj, Out out);
86
+ static void dump_data_obj(VALUE obj, int depth, Out out);
86
87
  static void dump_obj_comp(VALUE obj, int depth, Out out);
87
88
  static void dump_obj_obj(VALUE obj, int depth, Out out);
88
89
  #if HAS_RSTRUCT
@@ -92,7 +93,8 @@ static void dump_struct_obj(VALUE obj, int depth, Out out);
92
93
  #if HAS_IVAR_HELPERS
93
94
  static int dump_attr_cb(ID key, VALUE value, Out out);
94
95
  #endif
95
- static void dump_obj_attrs(VALUE obj, int with_class, slot_t id, int depth, Out out);
96
+ static void dump_obj_attrs(VALUE obj, VALUE clas, slot_t id, int depth, Out out);
97
+ static void dump_odd(VALUE obj, Odd odd, VALUE clas, int depth, Out out);
96
98
 
97
99
  static void grow(Out out, size_t len);
98
100
  static size_t hibit_friendly_size(const u_char *str, size_t len);
@@ -111,7 +113,7 @@ static const char hex_chars[17] = "0123456789abcdef";
111
113
 
112
114
  static char hibit_friendly_chars[256] = "\
113
115
  66666666222622666666666666666666\
114
- 11211111111111121111111111111111\
116
+ 11211111111111111111111111111111\
115
117
  11111111111111111111111111112111\
116
118
  11111111111111111111111111111111\
117
119
  11111111111111111111111111111111\
@@ -223,6 +225,16 @@ dump_hex(u_char c, Out out) {
223
225
  *out->cur++ = hex_chars[d];
224
226
  }
225
227
 
228
+ static void
229
+ dump_raw(const char *str, size_t cnt, Out out) {
230
+ if (out->end - out->cur <= (long)cnt + 10) {
231
+ grow(out, cnt + 10);
232
+ }
233
+ memcpy(out->cur, str, cnt);
234
+ out->cur += cnt;
235
+ *out->cur = '\0';
236
+ }
237
+
226
238
  const char*
227
239
  dump_unicode(const char *str, const char *end, Out out) {
228
240
  uint32_t code = 0;
@@ -635,6 +647,7 @@ dump_array(VALUE a, int depth, Out out) {
635
647
  }
636
648
  if (0 < out->opts->dump_opts->indent_size) {
637
649
  int i;
650
+
638
651
  for (i = depth; 0 < i; i--) {
639
652
  strcpy(out->cur, out->opts->dump_opts->indent);
640
653
  out->cur += out->opts->dump_opts->indent_size;
@@ -934,18 +947,27 @@ dump_time(VALUE obj, Out out) {
934
947
 
935
948
  static void
936
949
  dump_data_comp(VALUE obj, Out out) {
937
- VALUE clas = rb_obj_class(obj);
950
+ VALUE clas = rb_obj_class(obj);
938
951
 
939
952
  if (rb_cTime == clas) {
940
953
  dump_time(obj, out);
941
954
  } else {
942
- dump_nil(out);
955
+ VALUE rstr;
956
+
957
+ if (oj_bigdecimal_class == clas) {
958
+ //rstr = rb_funcall(obj, oj_to_s_id, 1, rb_intern("E"));
959
+ rstr = rb_funcall(obj, oj_to_s_id, 0);
960
+ dump_raw(StringValuePtr(rstr), RSTRING_LEN(rstr), out);
961
+ } else {
962
+ rstr = rb_funcall(obj, oj_to_s_id, 0);
963
+ dump_cstr(StringValuePtr(rstr), RSTRING_LEN(rstr), 0, 0, out);
964
+ }
943
965
  }
944
966
  }
945
967
 
946
968
  static void
947
- dump_data_obj(VALUE obj, Out out) {
948
- VALUE clas = rb_obj_class(obj);
969
+ dump_data_obj(VALUE obj, int depth, Out out) {
970
+ VALUE clas = rb_obj_class(obj);
949
971
 
950
972
  if (rb_cTime == clas) {
951
973
  if (out->end - out->cur <= 6) {
@@ -961,7 +983,14 @@ dump_data_obj(VALUE obj, Out out) {
961
983
  *out->cur++ = '}';
962
984
  *out->cur = '\0';
963
985
  } else {
964
- dump_nil(out);
986
+ VALUE clas = rb_obj_class(obj);
987
+ Odd odd = oj_get_odd(clas);
988
+
989
+ if (0 == odd) {
990
+ dump_nil(out);
991
+ } else {
992
+ dump_odd(obj, odd, clas, depth + 1, out);
993
+ }
965
994
  }
966
995
  }
967
996
 
@@ -987,7 +1016,21 @@ dump_obj_comp(VALUE obj, int depth, Out out) {
987
1016
  memcpy(out->cur, s, len);
988
1017
  out->cur += len;
989
1018
  } else {
990
- dump_obj_attrs(obj, 0, 0, depth, out);
1019
+ VALUE clas = rb_obj_class(obj);
1020
+
1021
+ if (oj_bigdecimal_class == clas) {
1022
+ VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1023
+
1024
+ dump_raw(StringValuePtr(rstr), RSTRING_LEN(rstr), out);
1025
+ } else {
1026
+ Odd odd = oj_get_odd(clas);
1027
+
1028
+ if (0 == odd) {
1029
+ dump_obj_attrs(obj, 0, 0, depth, out);
1030
+ } else {
1031
+ dump_odd(obj, odd, 0, depth + 1, out);
1032
+ }
1033
+ }
991
1034
  }
992
1035
  *out->cur = '\0';
993
1036
  }
@@ -997,7 +1040,14 @@ dump_obj_obj(VALUE obj, int depth, Out out) {
997
1040
  long id = check_circular(obj, out);
998
1041
 
999
1042
  if (0 <= id) {
1000
- dump_obj_attrs(obj, 1, id, depth, out);
1043
+ VALUE clas = rb_obj_class(obj);
1044
+ Odd odd = oj_get_odd(clas);
1045
+
1046
+ if (0 == odd) {
1047
+ dump_obj_attrs(obj, clas, id, depth, out);
1048
+ } else {
1049
+ dump_odd(obj, odd, clas, depth + 1, out);
1050
+ }
1001
1051
  }
1002
1052
  }
1003
1053
 
@@ -1033,7 +1083,7 @@ dump_attr_cb(ID key, VALUE value, Out out) {
1033
1083
  #endif
1034
1084
 
1035
1085
  static void
1036
- dump_obj_attrs(VALUE obj, int with_class, slot_t id, int depth, Out out) {
1086
+ dump_obj_attrs(VALUE obj, VALUE clas, slot_t id, int depth, Out out) {
1037
1087
  size_t size;
1038
1088
  int d2 = depth + 1;
1039
1089
 
@@ -1041,8 +1091,8 @@ dump_obj_attrs(VALUE obj, int with_class, slot_t id, int depth, Out out) {
1041
1091
  grow(out, 2);
1042
1092
  }
1043
1093
  *out->cur++ = '{';
1044
- if (with_class) {
1045
- const char *class_name = rb_class2name(rb_obj_class(obj));
1094
+ if (0 != clas) {
1095
+ const char *class_name = rb_class2name(clas);
1046
1096
  int clen = (int)strlen(class_name);
1047
1097
 
1048
1098
  size = d2 * out->indent + clen + 10;
@@ -1085,13 +1135,15 @@ dump_obj_attrs(VALUE obj, int with_class, slot_t id, int depth, Out out) {
1085
1135
 
1086
1136
  cnt = (int)RARRAY_LEN(vars);
1087
1137
  #endif
1088
- if (with_class && 0 < cnt) {
1138
+ if (0 != clas && 0 < cnt) {
1089
1139
  *out->cur++ = ',';
1090
1140
  }
1091
1141
  out->depth = depth + 1;
1092
1142
  #if HAS_IVAR_HELPERS
1093
1143
  rb_ivar_foreach(obj, dump_attr_cb, (VALUE)out);
1094
- out->cur--; // backup to overwrite last comma
1144
+ if (',' == *(out->cur - 1)) {
1145
+ out->cur--; // backup to overwrite last comma
1146
+ }
1095
1147
  #else
1096
1148
  size = d2 * out->indent + 1;
1097
1149
  for (i = cnt; 0 < i; i--, np++) {
@@ -1197,6 +1249,56 @@ dump_struct_obj(VALUE obj, int depth, Out out) {
1197
1249
  }
1198
1250
  #endif
1199
1251
 
1252
+ static void
1253
+ dump_odd(VALUE obj, Odd odd, VALUE clas, int depth, Out out) {
1254
+ ID *idp;
1255
+ VALUE v;
1256
+ const char *name;
1257
+ size_t size;
1258
+ int d2 = depth + 1;
1259
+
1260
+ if (out->end - out->cur <= 2) {
1261
+ grow(out, 2);
1262
+ }
1263
+ *out->cur++ = '{';
1264
+ if (0 != clas) {
1265
+ const char *class_name = rb_class2name(clas);
1266
+ int clen = (int)strlen(class_name);
1267
+
1268
+ size = d2 * out->indent + clen + 10;
1269
+ if (out->end - out->cur <= (long)size) {
1270
+ grow(out, size);
1271
+ }
1272
+ fill_indent(out, d2);
1273
+ *out->cur++ = '"';
1274
+ *out->cur++ = '^';
1275
+ *out->cur++ = 'O';
1276
+ *out->cur++ = '"';
1277
+ *out->cur++ = ':';
1278
+ dump_cstr(class_name, clen, 0, 0, out);
1279
+ *out->cur++ = ',';
1280
+ }
1281
+ size = d2 * out->indent + 1;
1282
+ for (idp = odd->attrs; 0 != *idp; idp++) {
1283
+ if (out->end - out->cur <= (long)size) {
1284
+ grow(out, size);
1285
+ }
1286
+ name = rb_id2name(*idp);
1287
+ v = rb_funcall(obj, *idp, 0);
1288
+ fill_indent(out, d2);
1289
+ dump_cstr(name, strlen(name), 0, 0, out);
1290
+ *out->cur++ = ':';
1291
+ dump_val(v, d2, out);
1292
+ if (out->end - out->cur <= 2) {
1293
+ grow(out, 2);
1294
+ }
1295
+ *out->cur++ = ',';
1296
+ }
1297
+ out->cur--;
1298
+ *out->cur++ = '}';
1299
+ *out->cur = '\0';
1300
+ }
1301
+
1200
1302
  static void
1201
1303
  raise_strict(VALUE obj) {
1202
1304
  rb_raise(rb_eTypeError, "Failed to dump %s Object to JSON in strict mode.\n", rb_class2name(rb_obj_class(obj)));
@@ -1240,6 +1342,9 @@ dump_val(VALUE obj, int depth, Out out) {
1240
1342
  default: dump_class_obj(obj, out); break;
1241
1343
  }
1242
1344
  break;
1345
+ #if (defined T_RATIONAL && defined RRATIONAL)
1346
+ case T_RATIONAL:
1347
+ #endif
1243
1348
  case T_OBJECT:
1244
1349
  switch (out->opts->mode) {
1245
1350
  case StrictMode: raise_strict(obj); break;
@@ -1255,7 +1360,7 @@ dump_val(VALUE obj, int depth, Out out) {
1255
1360
  case NullMode: dump_nil(out); break;
1256
1361
  case CompatMode: dump_data_comp(obj, out); break;
1257
1362
  case ObjectMode:
1258
- default: dump_data_obj(obj, out); break;
1363
+ default: dump_data_obj(obj, depth, out); break;
1259
1364
  }
1260
1365
  break;
1261
1366
  #if HAS_RSTRUCT
@@ -1271,9 +1376,6 @@ dump_val(VALUE obj, int depth, Out out) {
1271
1376
  #endif
1272
1377
  #if (defined T_COMPLEX && defined RCOMPLEX)
1273
1378
  case T_COMPLEX:
1274
- #endif
1275
- #if (defined T_RATIONAL && defined RRATIONAL)
1276
- case T_RATIONAL:
1277
1379
  #endif
1278
1380
  case T_REGEXP:
1279
1381
  switch (out->opts->mode) {
@@ -24,6 +24,7 @@ dflags = {
24
24
  'HAS_IVAR_HELPERS' => ('ruby' == type && ('1' == version[0] && '9' == version[1]) || '2' <= version[0]) ? 1 : 0,
25
25
  'HAS_PROC_WITH_BLOCK' => ('ruby' == type && ('1' == version[0] && '9' == version[1]) || '2' <= version[0]) ? 1 : 0,
26
26
  'HAS_TOP_LEVEL_ST_H' => ('ree' == type || ('ruby' == type && '1' == version[0] && '8' == version[1])) ? 1 : 0,
27
+ 'SAFE_CACHE' => nil,
27
28
  }
28
29
  # This is a monster hack to get around issues with 1.9.3-p0 on CentOS 5.4. SO
29
30
  # some reason math.h and string.h contents are not processed. Might be a
@@ -28,6 +28,7 @@
28
28
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
29
  */
30
30
 
31
+ #include <pthread.h> // TBD LOCK
31
32
  #include <stdlib.h>
32
33
  #include <stdio.h>
33
34
  #include <string.h>
@@ -157,6 +158,9 @@ classname2class(const char *name, ParseInfo pi) {
157
158
  VALUE *slot;
158
159
  int auto_define = (Yes == pi->options->auto_define);
159
160
 
161
+ #ifdef SAFE_CACHE
162
+ pthread_mutex_lock(&oj_cache_mutex);
163
+ #endif
160
164
  if (Qundef == (clas = oj_cache_get(oj_class_cache, name, &slot))) {
161
165
  char class_name[1024];
162
166
  char *s;
@@ -183,6 +187,9 @@ classname2class(const char *name, ParseInfo pi) {
183
187
  *slot = clas;
184
188
  }
185
189
  }
190
+ #ifdef SAFE_CACHE
191
+ pthread_mutex_unlock(&oj_cache_mutex);
192
+ #endif
186
193
  return clas;
187
194
  }
188
195
 
@@ -370,6 +377,9 @@ read_obj(ParseInfo pi) {
370
377
  int obj_type = T_NONE;
371
378
  const char *json_class_name = 0;
372
379
  Mode mode = pi->options->mode;
380
+ Odd odd = 0;
381
+ VALUE odd_args[MAX_ODD_ARGS];
382
+ VALUE *vp;
373
383
 
374
384
  pi->s++;
375
385
  next_non_white(pi);
@@ -420,6 +430,16 @@ read_obj(ParseInfo pi) {
420
430
  obj_type = T_OBJECT;
421
431
  key = Qundef;
422
432
  break;
433
+ case 'O': // Odd class
434
+ if (0 == (odd = oj_get_odd(read_next(pi, T_CLASS)))) {
435
+ raise_error("Not a valid build in class.", pi->str, pi->s);
436
+ }
437
+ obj = Qundef;
438
+ key = Qundef;
439
+ for (vp = odd_args + MAX_ODD_ARGS - 1; odd_args <=vp; vp--) {
440
+ *vp = Qnil;
441
+ }
442
+ break;
423
443
  case 'u': // Struct
424
444
  obj = read_next(pi, T_STRUCT);
425
445
  obj_type = T_STRUCT;
@@ -435,7 +455,7 @@ read_obj(ParseInfo pi) {
435
455
  if (Qundef == val && Qundef == (val = read_next(pi, 0))) {
436
456
  raise_error("unexpected character", pi->str, pi->s);
437
457
  }
438
- if (Qundef == obj) {
458
+ if (Qundef == obj && 0 == odd) {
439
459
  obj = rb_hash_new();
440
460
  obj_type = T_HASH;
441
461
  }
@@ -455,10 +475,25 @@ read_obj(ParseInfo pi) {
455
475
  }
456
476
  }
457
477
  if (Qundef != key) {
458
- if (T_OBJECT == obj_type) {
478
+ if (0 != odd) {
479
+ ID *idp;
480
+
481
+ for (idp = odd->attrs, vp = odd_args; 0 != *idp; idp++, vp++) {
482
+ if (0 == strcmp(rb_id2name(*idp), ks)) {
483
+ *vp = val;
484
+ break;
485
+ }
486
+ }
487
+ if (odd_args + MAX_ODD_ARGS <= vp) {
488
+ raise_error("invalid attribute", pi->str, pi->s);
489
+ }
490
+ } else if (T_OBJECT == obj_type) {
459
491
  VALUE *slot;
460
492
  ID var_id;
461
493
 
494
+ #ifdef SAFE_CACHE
495
+ pthread_mutex_lock(&oj_cache_mutex);
496
+ #endif
462
497
  if (Qundef == (var_id = oj_cache_get(oj_attr_cache, ks, &slot))) {
463
498
  char attr[1024];
464
499
 
@@ -472,6 +507,9 @@ read_obj(ParseInfo pi) {
472
507
  var_id = rb_intern(attr);
473
508
  *slot = var_id;
474
509
  }
510
+ #ifdef SAFE_CACHE
511
+ pthread_mutex_unlock(&oj_cache_mutex);
512
+ #endif
475
513
  rb_ivar_set(obj, var_id, val);
476
514
  } else if (T_HASH == obj_type) {
477
515
  if (Yes == pi->options->sym_key) {
@@ -501,7 +539,9 @@ read_obj(ParseInfo pi) {
501
539
  raise_error("invalid format, expected , or } while in an object", pi->str, pi->s);
502
540
  }
503
541
  }
504
- if (0 != json_class_name) {
542
+ if (0 != odd) {
543
+ obj = rb_funcall2(odd->create_obj, odd->create_op, odd->attr_cnt, odd_args);
544
+ } else if (0 != json_class_name) {
505
545
  VALUE clas = classname2class(json_class_name, pi);
506
546
  VALUE args[1];
507
547
 
@@ -625,7 +665,7 @@ read_str(ParseInfo pi, int hint) {
625
665
  } else if (ObjectMode == pi->options->mode && '^' == *text && '\0' != text[2]) {
626
666
  char c1 = text[1];
627
667
 
628
- if ('r' == c1) {
668
+ if ('r' == c1 && 0 != pi->circ_array) {
629
669
  obj = circ_array_get(pi->circ_array, read_ulong(text + 2, pi));
630
670
  } else if ('i' == c1) {
631
671
  obj = ULONG2NUM(read_ulong(text + 2, pi));
@@ -691,6 +731,9 @@ read_num(ParseInfo pi) {
691
731
  for (; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
692
732
  a = a * 10 + (*pi->s - '0');
693
733
  div *= 10;
734
+ if (NUM_MAX <= div) {
735
+ big = 1;
736
+ }
694
737
  }
695
738
  }
696
739
  if ('e' == *pi->s || 'E' == *pi->s) {
@@ -703,6 +746,9 @@ read_num(ParseInfo pi) {
703
746
  }
704
747
  for (; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
705
748
  e = e * 10 + (*pi->s - '0');
749
+ if (NUM_MAX <= e) {
750
+ big = 1;
751
+ }
706
752
  }
707
753
  }
708
754
  if (0 == e && 0 == a && 1 == div) {
@@ -721,22 +767,33 @@ read_num(ParseInfo pi) {
721
767
  }
722
768
  return LONG2NUM(n);
723
769
  }
724
- } else {
725
- double d = (double)n + (double)a / (double)div;
770
+ } else { // decimal
771
+ if (big) {
772
+ char c = *pi->s;
773
+ VALUE num;
774
+
775
+ *pi->s = '\0';
776
+ num = rb_funcall(oj_bigdecimal_class, oj_new_id, 1, rb_str_new2(start));
777
+ *pi->s = c;
726
778
 
727
- if (neg) {
728
- d = -d;
729
- }
730
- if (1 < big) {
731
- e += big - 1;
732
- }
733
- if (0 != e) {
734
- if (eneg) {
735
- e = -e;
779
+ return num;
780
+ } else {
781
+ double d = (double)n + (double)a / (double)div;
782
+
783
+ if (neg) {
784
+ d = -d;
785
+ }
786
+ if (1 < big) {
787
+ e += big - 1;
788
+ }
789
+ if (0 != e) {
790
+ if (eneg) {
791
+ e = -e;
792
+ }
793
+ d *= pow(10.0, e);
736
794
  }
737
- d *= pow(10.0, e);
795
+ return rb_float_new(d);
738
796
  }
739
- return rb_float_new(d);
740
797
  }
741
798
  }
742
799