Permalink
Browse files

adding new encodings

  • Loading branch information...
1 parent 2c63491 commit c9bb99f2c3807ae8edc80ea9d4b8dec379c39f4b Peter Ohler committed Jun 19, 2012
Showing with 222 additions and 38 deletions.
  1. +9 −4 README.md
  2. +77 −8 ext/oj/dump.c
  3. +2 −0 ext/oj/extconf.rb
  4. +60 −13 ext/oj/load.c
  5. +11 −2 ext/oj/oj.c
  6. +4 −0 ext/oj/oj.h
  7. +4 −5 notes
  8. +55 −6 test/tests.rb
View
@@ -24,11 +24,15 @@ A fast JSON parser and Object marshaller as a Ruby gem.
## <a name="release">Release Notes</a>
-### Release 1.2.11
+### Release 1.2.10
- Added check for circular on loading of circular dumped JSON.
- -
+ - Added support for direct serialization of BigDecimal, Date, and DateTime.
+
+ - Added json.rb to $" in mimic mode to avoid pulling in the real JSON by accident.
+
+ - Oj is now thread safe for all functions.
## <a name="description">Description</a>
@@ -354,9 +358,10 @@ excaping the first character so that it appears as `\u005e` or `\u003a` instead
class. The sequence `{"^c":"Oj::Bag"}` is read as the Oj::Bag class.
6. A `"^t"` JSON Object key indicates the value should be converted to a Ruby
-Time. The sequence `{"^t":1325775487.000000}` is read as Jan 5, 2012 at 23:58:07.
+Time. The sequence `{"^t":1325775487.000000}` is read as Jan 5, 2012 at
+23:58:07. Similarly, `"^d"` is for a Date and `"^T"` is for a DateTime.
-87. A `"^o"` JSON Object key indicates the value should be converted to a Ruby
+7. A `"^o"` JSON Object key indicates the value should be converted to a Ruby
Object. The first entry in the JSON Object must be a class with the `"^o"`
key. After that each entry is treated as a variable of the Object where the
key is the variable name without the preceeding `@`. An example is
View
@@ -67,6 +67,7 @@ static void dump_false(Out out);
static void dump_fixnum(VALUE obj, Out out);
static void dump_bignum(VALUE obj, Out out);
static void dump_float(VALUE obj, Out out);
+static void dump_raw(const char *str, size_t cnt, Out out);
static void dump_cstr(const char *str, size_t cnt, int is_sym, int escape1, Out out);
static void dump_hex(u_char c, Out out);
static void dump_str_comp(VALUE obj, Out out);
@@ -223,6 +224,16 @@ dump_hex(u_char c, Out out) {
*out->cur++ = hex_chars[d];
}
+static void
+dump_raw(const char *str, size_t cnt, Out out) {
+ if (out->end - out->cur <= (long)cnt + 10) {
+ grow(out, cnt + 10);
+ }
+ memcpy(out->cur, str, cnt);
+ out->cur += cnt;
+ *out->cur = '\0';
+}
+
const char*
dump_unicode(const char *str, const char *end, Out out) {
uint32_t code = 0;
@@ -635,6 +646,7 @@ dump_array(VALUE a, int depth, Out out) {
}
if (0 < out->opts->dump_opts->indent_size) {
int i;
+
for (i = depth; 0 < i; i--) {
strcpy(out->cur, out->opts->dump_opts->indent);
out->cur += out->opts->dump_opts->indent_size;
@@ -934,30 +946,44 @@ dump_time(VALUE obj, Out out) {
static void
dump_data_comp(VALUE obj, Out out) {
- VALUE clas = rb_obj_class(obj);
+ VALUE clas = rb_obj_class(obj);
if (rb_cTime == clas) {
dump_time(obj, out);
- // TBD BigDecimal
- // TBD Date
- // TBD DateTime
+ } else if (oj_date_class == clas || oj_datetime_class == clas) {
+ dump_time(rb_funcall(obj, oj_to_time_id, 0), out);
} else {
- dump_nil(out);
+ VALUE rstr;
+
+ if (oj_bigdecimal_class == clas) {
+ rstr = rb_funcall(obj, oj_to_s_id, 1, rb_intern("E"));
+ dump_raw(StringValuePtr(rstr), RSTRING_LEN(rstr), out);
+ } else {
+ rstr = rb_any_to_s(obj);
+ dump_cstr(StringValuePtr(rstr), RSTRING_LEN(rstr), 0, 0, out);
+ }
+ //dump_nil(out);
}
}
static void
dump_data_obj(VALUE obj, Out out) {
- VALUE clas = rb_obj_class(obj);
+ VALUE clas = rb_obj_class(obj);
+ char type = 't';
+ if (oj_date_class == clas || oj_datetime_class == clas) {
+ type = (oj_date_class == clas) ? 'd' : 'T';
+ clas = rb_cTime;
+ obj = rb_funcall(obj, oj_to_time_id, 0);
+ }
if (rb_cTime == clas) {
if (out->end - out->cur <= 6) {
grow(out, 6);
}
*out->cur++ = '{';
*out->cur++ = '"';
*out->cur++ = '^';
- *out->cur++ = 't';
+ *out->cur++ = type;
*out->cur++ = '"';
*out->cur++ = ':';
dump_time(obj, out);
@@ -990,7 +1016,21 @@ dump_obj_comp(VALUE obj, int depth, Out out) {
memcpy(out->cur, s, len);
out->cur += len;
} else {
+#if DATE_IS_DATA
dump_obj_attrs(obj, 0, 0, depth, out);
+#else
+ VALUE clas = rb_obj_class(obj);
+
+ if (oj_date_class == clas || oj_datetime_class == clas) {
+#if HAS_TO_TIME
+ dump_time(rb_funcall(obj, oj_to_time_id, 0), out);
+#else
+ dump_time(rb_funcall(rb_cTime, rb_intern("parse"), 1, rb_funcall(obj, oj_to_s_id, 0)), out);
+#endif
+ } else {
+ dump_obj_attrs(obj, 0, 0, depth, out);
+ }
+#endif
}
*out->cur = '\0';
}
@@ -1000,7 +1040,34 @@ dump_obj_obj(VALUE obj, int depth, Out out) {
long id = check_circular(obj, out);
if (0 <= id) {
+#if DATE_IS_DATA
dump_obj_attrs(obj, 1, id, depth, out);
+#else
+ VALUE clas = rb_obj_class(obj);
+
+ if (oj_date_class == clas || oj_datetime_class == clas) {
+ char type = (oj_date_class == clas) ? 'd' : 'T';
+
+ if (out->end - out->cur <= 6) {
+ grow(out, 6);
+ }
+ *out->cur++ = '{';
+ *out->cur++ = '"';
+ *out->cur++ = '^';
+ *out->cur++ = type;
+ *out->cur++ = '"';
+ *out->cur++ = ':';
+#if HAS_TO_TIME
+ dump_time(rb_funcall(obj, oj_to_time_id, 0), out);
+#else
+ dump_time(rb_funcall(rb_cTime, rb_intern("parse"), 1, rb_funcall(obj, oj_to_s_id, 0)), out);
+#endif
+ *out->cur++ = '}';
+ *out->cur = '\0';
+ } else {
+ dump_obj_attrs(obj, 1, id, depth, out);
+ }
+#endif
}
}
@@ -1094,7 +1161,9 @@ dump_obj_attrs(VALUE obj, int with_class, slot_t id, int depth, Out out) {
out->depth = depth + 1;
#if HAS_IVAR_HELPERS
rb_ivar_foreach(obj, dump_attr_cb, (VALUE)out);
- out->cur--; // backup to overwrite last comma
+ if (',' == *(out->cur - 1)) {
+ out->cur--; // backup to overwrite last comma
+ }
#else
size = d2 * out->indent + 1;
for (i = cnt; 0 < i; i--, np++) {
View
@@ -24,6 +24,8 @@
'HAS_IVAR_HELPERS' => ('ruby' == type && ('1' == version[0] && '9' == version[1]) || '2' <= version[0]) ? 1 : 0,
'HAS_PROC_WITH_BLOCK' => ('ruby' == type && ('1' == version[0] && '9' == version[1]) || '2' <= version[0]) ? 1 : 0,
'HAS_TOP_LEVEL_ST_H' => ('ree' == type || ('ruby' == type && '1' == version[0] && '8' == version[1])) ? 1 : 0,
+ 'DATE_IS_DATA' => ('ruby' == type && '1' == version[0] && '9' == version[1] && '3' <= version[0]) ? 1 : 0,
+ 'HAS_TO_TIME' => ('ruby' == type && '1' == version[0] && '9' == version[1]) ? 1 : 0,
'SAFE_CACHE' => nil,
}
# This is a monster hack to get around issues with 1.9.3-p0 on CentOS 5.4. SO
View
@@ -410,6 +410,35 @@ read_obj(ParseInfo pi) {
obj = read_next(pi, TIME_HINT); // raises if can not convert to Time
key = Qundef;
break;
+ case 'd': // Date
+ obj = read_next(pi, TIME_HINT); // raises if can not convert to Time
+ // TBD change to utc for some rubies (rbx-1.2.4, rbx-2.0.0-dev)
+#if HAS_TO_TIME
+ obj = rb_funcall(obj, rb_intern("to_date"), 0);
+#else
+ obj = rb_funcall(oj_date_class, rb_intern("new"), 3,
+ rb_funcall(obj, rb_intern("year"), 0),
+ rb_funcall(obj, rb_intern("month"), 0),
+ rb_funcall(obj, rb_intern("mday"), 0));
+#endif
+ key = Qundef;
+ break;
+ case 'T': // DateTime
+ obj = read_next(pi, TIME_HINT); // raises if can not convert to Time
+ obj = rb_funcall(obj, rb_intern("getutc"), 0);
+#if HAS_TO_TIME
+ obj = rb_funcall(obj, rb_intern("to_datetime"), 0);
+#else
+ obj = rb_funcall(oj_datetime_class, rb_intern("new"), 6,
+ rb_funcall(obj, rb_intern("year"), 0),
+ rb_funcall(obj, rb_intern("month"), 0),
+ rb_funcall(obj, rb_intern("mday"), 0),
+ rb_funcall(obj, rb_intern("hour"), 0),
+ rb_funcall(obj, rb_intern("min"), 0),
+ rb_funcall(obj, rb_intern("sec"), 0));
+#endif
+ key = Qundef;
+ break;
case 'c': // Class
obj = read_next(pi, T_CLASS);
key = Qundef;
@@ -432,6 +461,7 @@ read_obj(ParseInfo pi) {
obj_type = T_STRUCT;
key = Qundef;
break;
+ // TBD d for Date, T for DateTime
default:
// handle later
break;
@@ -704,6 +734,9 @@ read_num(ParseInfo pi) {
for (; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
a = a * 10 + (*pi->s - '0');
div *= 10;
+ if (NUM_MAX <= div) {
+ big = 1;
+ }
}
}
if ('e' == *pi->s || 'E' == *pi->s) {
@@ -716,6 +749,9 @@ read_num(ParseInfo pi) {
}
for (; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
e = e * 10 + (*pi->s - '0');
+ if (NUM_MAX <= e) {
+ big = 1;
+ }
}
}
if (0 == e && 0 == a && 1 == div) {
@@ -734,22 +770,33 @@ read_num(ParseInfo pi) {
}
return LONG2NUM(n);
}
- } else {
- double d = (double)n + (double)a / (double)div;
+ } else { // decimal
+ if (big) {
+ char c = *pi->s;
+ VALUE num;
+
+ *pi->s = '\0';
+ num = rb_funcall(oj_bigdecimal_class, rb_intern("new"), 1, rb_str_new2(start));
+ *pi->s = c;
- if (neg) {
- d = -d;
- }
- if (1 < big) {
- e += big - 1;
- }
- if (0 != e) {
- if (eneg) {
- e = -e;
+ return num;
+ } else {
+ double d = (double)n + (double)a / (double)div;
+
+ if (neg) {
+ d = -d;
+ }
+ if (1 < big) {
+ e += big - 1;
+ }
+ if (0 != e) {
+ if (eneg) {
+ e = -e;
+ }
+ d *= pow(10.0, e);
}
- d *= pow(10.0, e);
+ return rb_float_new(d);
}
- return rb_float_new(d);
}
}
View
@@ -60,14 +60,18 @@ ID oj_read_id;
ID oj_string_id;
ID oj_to_hash_id;
ID oj_to_json_id;
+ID oj_to_s_id;
ID oj_to_sym_id;
-ID oj_write_id;
+ID oj_to_time_id;
ID oj_tv_nsec_id;
ID oj_tv_sec_id;
ID oj_tv_usec_id;
+ID oj_write_id;
VALUE oj_bag_class;
+VALUE oj_bigdecimal_class;
VALUE oj_date_class;
+VALUE oj_datetime_class;
VALUE oj_stringio_class;
VALUE oj_struct_class;
VALUE oj_time_class;
@@ -771,6 +775,7 @@ void Init_oj() {
rb_require("time");
rb_require("date");
+ rb_require("bigdecimal");
rb_require("stringio");
rb_define_module_function(Oj, "default_options", get_def_opts, 0);
@@ -790,16 +795,20 @@ void Init_oj() {
oj_string_id = rb_intern("string");
oj_to_hash_id = rb_intern("to_hash");
oj_to_json_id = rb_intern("to_json");
+ oj_to_s_id = rb_intern("to_s");
oj_to_sym_id = rb_intern("to_sym");
- oj_write_id = rb_intern("write");
+ oj_to_time_id = rb_intern("to_time");
oj_tv_nsec_id = rb_intern("tv_nsec");
oj_tv_sec_id = rb_intern("tv_sec");
oj_tv_usec_id = rb_intern("tv_usec");
+ oj_write_id = rb_intern("write");
oj_bag_class = rb_const_get_at(Oj, rb_intern("Bag"));
oj_struct_class = rb_const_get(rb_cObject, rb_intern("Struct"));
oj_time_class = rb_const_get(rb_cObject, rb_intern("Time"));
oj_date_class = rb_const_get(rb_cObject, rb_intern("Date"));
+ oj_datetime_class = rb_const_get(rb_cObject, rb_intern("DateTime"));
+ oj_bigdecimal_class = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
oj_stringio_class = rb_const_get(rb_cObject, rb_intern("StringIO"));
ascii_only_sym = ID2SYM(rb_intern("ascii_only")); rb_gc_register_address(&ascii_only_sym);
View
@@ -141,7 +141,9 @@ extern rb_encoding *oj_utf8_encoding;
#endif
extern VALUE oj_bag_class;
+extern VALUE oj_bigdecimal_class;
extern VALUE oj_date_class;
+extern VALUE oj_datetime_class;
extern VALUE oj_doc_class;
extern VALUE oj_stringio_class;
extern VALUE oj_struct_class;
@@ -155,7 +157,9 @@ extern ID oj_json_create_id;
extern ID oj_string_id;
extern ID oj_to_hash_id;
extern ID oj_to_json_id;
+extern ID oj_to_s_id;
extern ID oj_to_sym_id;
+extern ID oj_to_time_id;
extern ID oj_tv_nsec_id;
extern ID oj_tv_sec_id;
extern ID oj_tv_usec_id;
View
9 notes
@@ -5,11 +5,10 @@
- next
- - add json.rb to $" in mimic
- - mutex around cache to allow thread safety
- - double check timing
- - if slower make comment in ext
- - should gc be blocked during calls?
+ - finish implementing BigDecimal for all rubies
+ - fix Date loading with RBX
+
+
- optimize read_hex in load.c
- add options for path in fetch
Oops, something went wrong.

0 comments on commit c9bb99f

Please sign in to comment.