ext/oj/load.c in oj-0.6.0 vs ext/oj/load.c in oj-0.7.0
- old
+ new
@@ -34,10 +34,14 @@
#include <math.h>
#include "ruby.h"
#include "oj.h"
+enum {
+ TIME_HINT = 0x0100,
+};
+
typedef struct _ParseInfo {
char *str; /* buffer being read from */
char *s; /* current position in buffer */
#ifdef HAVE_RUBY_ENCODING_H
rb_encoding *encoding;
@@ -45,15 +49,17 @@
void *encoding;
#endif
Options options;
} *ParseInfo;
-static VALUE read_next(ParseInfo pi);
+static VALUE classname2class(const char *name, ParseInfo pi);
+static VALUE read_next(ParseInfo pi, int hint);
static VALUE read_obj(ParseInfo pi);
static VALUE read_array(ParseInfo pi);
-static VALUE read_str(ParseInfo pi);
+static VALUE read_str(ParseInfo pi, int hint);
static VALUE read_num(ParseInfo pi);
+static VALUE read_time(ParseInfo pi);
static VALUE read_true(ParseInfo pi);
static VALUE read_false(ParseInfo pi);
static VALUE read_nil(ParseInfo pi);
static void next_non_white(ParseInfo pi);
static char* read_quoted_value(ParseInfo pi);
@@ -103,10 +109,90 @@
break;
}
}
}
+inline static VALUE
+resolve_classname(VALUE mod, const char *class_name, int create) {
+ VALUE clas;
+ ID ci = rb_intern(class_name);
+
+ if (rb_const_defined_at(mod, ci) || !create) {
+ clas = rb_const_get_at(mod, ci);
+ } else {
+ //clas = rb_define_class_under(mod, class_name, oj_bag_clas);
+ clas = rb_const_get_at(mod, ci); // TBD temp
+ }
+ return clas;
+}
+
+inline static VALUE
+classname2obj(const char *name, ParseInfo pi) {
+ VALUE clas = classname2class(name, pi);
+
+ if (Qundef == clas) {
+ return Qnil;
+ } else {
+ return rb_obj_alloc(clas);
+ }
+}
+
+static VALUE
+classname2class(const char *name, ParseInfo pi) {
+ VALUE clas;
+ int create = 0; // TBD from options
+
+#if 1
+ VALUE *slot;
+
+ if (Qundef == (clas = oj_cache_get(oj_class_cache, name, &slot))) {
+ char class_name[1024];
+ char *s;
+ const char *n = name;
+
+ clas = rb_cObject;
+ for (s = class_name; '\0' != *n; n++) {
+ if (':' == *n) {
+ *s = '\0';
+ n++;
+ if (Qundef == (clas = resolve_classname(clas, class_name, create))) {
+ return Qundef;
+ }
+ s = class_name;
+ } else {
+ *s++ = *n;
+ }
+ }
+ *s = '\0';
+ if (Qundef != (clas = resolve_classname(clas, class_name, create))) {
+ *slot = clas;
+ }
+ }
+#else
+ char class_name[1024];
+ char *s;
+ const char *n = name;
+
+ clas = rb_cObject;
+ for (s = class_name; '\0' != *n; n++) {
+ if (':' == *n) {
+ *s = '\0';
+ n++;
+ if (Qundef == (clas = resolve_classname(clas, class_name, create))) {
+ return Qundef;
+ }
+ s = class_name;
+ } else {
+ *s++ = *n;
+ }
+ }
+ *s = '\0';
+ clas = resolve_classname(clas, class_name, create);
+#endif
+ return clas;
+}
+
VALUE
oj_parse(char *json, Options options) {
VALUE obj;
struct _ParseInfo pi;
@@ -120,22 +206,22 @@
pi.encoding = ('\0' == *options->encoding) ? 0 : rb_enc_find(options->encoding);
#else
pi.encoding = 0;
#endif
pi.options = options;
- if (Qundef == (obj = read_next(&pi))) {
+ if (Qundef == (obj = read_next(&pi, 0))) {
raise_error("no object read", pi.str, pi.s);
}
next_non_white(&pi); // skip white space
if ('\0' != *pi.s) {
raise_error("invalid format, extra characters", pi.str, pi.s);
}
return obj;
}
static VALUE
-read_next(ParseInfo pi) {
+read_next(ParseInfo pi, int hint) {
VALUE obj;
next_non_white(pi); // skip white space
switch (*pi->s) {
case '{':
@@ -143,11 +229,11 @@
break;
case '[':
obj = read_array(pi);
break;
case '"':
- obj = read_str(pi);
+ obj = read_str(pi, hint);
break;
case '+':
case '-':
case '0':
case '1':
@@ -157,11 +243,15 @@
case '5':
case '6':
case '7':
case '8':
case '9':
- obj = read_num(pi);
+ if (TIME_HINT == hint) {
+ obj = read_time(pi);
+ } else {
+ obj = read_num(pi);
+ }
break;
case 't':
obj = read_true(pi);
break;
case 'f':
@@ -183,42 +273,115 @@
static VALUE
read_obj(ParseInfo pi) {
VALUE obj = Qundef;
VALUE key = Qundef;
VALUE val = Qundef;
+ const char *ks;
+ int obj_type = T_NONE;
pi->s++;
next_non_white(pi);
if ('}' == *pi->s) {
pi->s++;
return rb_hash_new();
}
while (1) {
next_non_white(pi);
- if ('"' != *pi->s || Qundef == (key = read_str(pi))) {
+ ks = 0;
+ key = Qundef;
+ val = Qundef;
+ if ('"' != *pi->s || Qundef == (key = read_str(pi, 0))) {
raise_error("unexpected character", pi->str, pi->s);
}
next_non_white(pi);
if (':' == *pi->s) {
pi->s++;
} else {
raise_error("invalid format, expected :", pi->str, pi->s);
}
- if (Qundef == (val = read_next(pi))) {
- raise_error("unexpected character", pi->str, pi->s);
+ if (T_STRING == rb_type(key)) {
+ ks = StringValuePtr(key);
+ } else {
+ ks = 0;
}
- if (Qundef == obj) {
- obj = rb_hash_new();
+ if (0 != ks && Qundef == obj && ObjectMode == pi->options->mode) {
+ if ('^' == *ks && '\0' == ks[2]) { // special directions
+ switch (ks[1]) {
+ case 't': // Time
+ obj = read_next(pi, TIME_HINT); // raises if can not convert to Time
+ key = Qundef;
+ break;
+ case 'c': // Class
+ obj = read_next(pi, T_CLASS);
+ key = Qundef;
+ break;
+ case 's': // Symbol
+ obj = read_next(pi, T_SYMBOL);
+ key = Qundef;
+ break;
+ case 'o': // Object
+ obj = read_next(pi, T_OBJECT);
+ obj_type = T_OBJECT;
+ key = Qundef;
+ break;
+ case 'i': // Id for circular reference
+ // TBD
+ default:
+ // handle later
+ break;
+ }
+ }
}
- rb_hash_aset(obj, key, val);
+ if (Qundef != key) {
+ if (Qundef == val && Qundef == (val = read_next(pi, 0))) {
+ raise_error("unexpected character", pi->str, pi->s);
+ }
+ if (ObjectMode == pi->options->mode &&
+ 0 != ks && '^' == *ks && '#' == ks[1] &&
+ (T_NONE == obj_type || T_HASH == obj_type) &&
+ rb_type(val) == T_ARRAY && 2 == RARRAY_LEN(val)) { // Hash entry
+ VALUE *np = RARRAY_PTR(val);
+
+ key = *np;
+ val = *(np + 1);
+ }
+ if (Qundef == obj) {
+ obj = rb_hash_new();
+ obj_type = T_HASH;
+ }
+ if (T_OBJECT == obj_type) {
+ VALUE *slot;
+ ID var_id;
+
+ if (Qundef == (var_id = oj_cache_get(oj_attr_cache, ks, &slot))) {
+ char attr[1024];
+
+ if ('~' == *ks) {
+ strncpy(attr, ks + 1, sizeof(attr) - 1);
+ } else {
+ *attr = '@';
+ strncpy(attr + 1, ks, sizeof(attr) - 2);
+ }
+ attr[sizeof(attr) - 1] = '\0';
+ var_id = rb_intern(attr);
+ *slot = var_id;
+ }
+ rb_ivar_set(obj, var_id, val);
+ } else if (T_HASH == obj_type) {
+ rb_hash_aset(obj, key, val);
+ } else {
+ raise_error("invalid Object format, too many Hash entries.", pi->str, pi->s);
+ }
+ }
next_non_white(pi);
if ('}' == *pi->s) {
pi->s++;
break;
} else if (',' == *pi->s) {
pi->s++;
} else {
+ //printf("*** '%s'\n", pi->s);
raise_error("invalid format, expected , or } while in an object", pi->str, pi->s);
}
}
return obj;
}
@@ -233,11 +396,11 @@
if (']' == *pi->s) {
pi->s++;
return a;
}
while (1) {
- if (Qundef == (e = read_next(pi))) {
+ if (Qundef == (e = read_next(pi, 0))) {
raise_error("unexpected character", pi->str, pi->s);
}
rb_ary_push(a, e);
next_non_white(pi); // skip white space
if (',' == *pi->s) {
@@ -251,20 +414,79 @@
}
return a;
}
static VALUE
-read_str(ParseInfo pi) {
+read_str(ParseInfo pi, int hint) {
char *text = read_quoted_value(pi);
- VALUE s = rb_str_new2(text);
+ VALUE obj;
-#ifdef HAVE_RUBY_ENCODING_H
- if (0 != pi->encoding) {
- rb_enc_associate(s, pi->encoding);
+ if (ObjectMode != pi->options->mode) {
+ hint = T_STRING;
}
+ switch (hint) {
+ case T_CLASS:
+ obj = classname2class(text, pi);
+ break;
+ case T_OBJECT:
+ obj = classname2obj(text, pi);
+ break;
+ case T_STRING:
+ obj = rb_str_new2(text);
+#ifdef HAVE_RUBY_ENCODING_H
+ if (0 != pi->encoding) {
+ rb_enc_associate(obj, pi->encoding);
+ }
#endif
- return s;
+ break;
+ case T_SYMBOL:
+#ifdef HAVE_RUBY_ENCODING_H
+ if (0 != pi->encoding) {
+ obj = rb_str_new2(text);
+ rb_enc_associate(obj, pi->encoding);
+ obj = rb_funcall(obj, oj_to_sym_id, 0);
+ } else {
+ obj = ID2SYM(rb_intern(text));
+ }
+#else
+ obj = ID2SYM(rb_intern(text));
+#endif
+ break;
+ case 0:
+ default:
+ if (':' == *text) {
+ if (':' == text[1]) { // escaped :, it s string
+ obj = rb_str_new2(text + 1);
+#ifdef HAVE_RUBY_ENCODING_H
+ if (0 != pi->encoding) {
+ rb_enc_associate(obj, pi->encoding);
+ }
+#endif
+ } else { // Symbol
+#ifdef HAVE_RUBY_ENCODING_H
+ if (0 != pi->encoding) {
+ obj = rb_str_new2(text + 1);
+ rb_enc_associate(obj, pi->encoding);
+ obj = rb_funcall(obj, oj_to_sym_id, 0);
+ } else {
+ obj = ID2SYM(rb_intern(text + 1));
+ }
+#else
+ obj = ID2SYM(rb_intern(text + 1));
+#endif
+ }
+ } else {
+ obj = rb_str_new2(text);
+#ifdef HAVE_RUBY_ENCODING_H
+ if (0 != pi->encoding) {
+ rb_enc_associate(obj, pi->encoding);
+ }
+#endif
+ }
+ break;
+ }
+ return obj;
}
#ifdef RUBINIUS
#define NUM_MAX 0x07FFFFFF
#else
@@ -340,9 +562,30 @@
}
d *= pow(10.0, e);
}
return DBL2NUM(d);
}
+}
+
+static VALUE
+read_time(ParseInfo pi) {
+ VALUE args[2];
+ long v = 0;
+ long v2 = 0;
+
+ for (; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
+ v = v * 10 + (*pi->s - '0');
+ }
+ if ('.' == *pi->s) {
+ pi->s++;
+ for (; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
+ v2 = v2 * 10 + (*pi->s - '0');
+ }
+ }
+ args[0] = LONG2NUM(v);
+ args[1] = LONG2NUM(v2);
+
+ return rb_funcall2(oj_time_class, oj_at_id, 2, args);
}
static VALUE
read_true(ParseInfo pi) {
pi->s++;