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++;