// Copyright (c) 2017 Peter Ohler. All rights reserved. // Licensed under the MIT License. See LICENSE file in the project root for license details. #include "code.h" #include "dump.h" inline static VALUE resolve_classname(VALUE mod, const char *classname) { VALUE clas = Qundef; ID ci = rb_intern(classname); if (rb_const_defined_at(mod, ci)) { clas = rb_const_get_at(mod, ci); } return clas; } static VALUE path2class(const char *name) { char class_name[1024]; VALUE clas; char *end = class_name + sizeof(class_name) - 1; char *s; const char *n = name; clas = rb_cObject; for (s = class_name; '\0' != *n; n++) { if (':' == *n) { *s = '\0'; n++; if (':' != *n) { return Qundef; } if (Qundef == (clas = resolve_classname(clas, class_name))) { return Qundef; } s = class_name; } else if (end <= s) { return Qundef; } else { *s++ = *n; } } *s = '\0'; return resolve_classname(clas, class_name); } bool oj_code_dump(Code codes, VALUE obj, int depth, Out out) { VALUE clas = rb_obj_class(obj); Code c = codes; for (; NULL != c->name; c++) { if (Qundef == c->clas) { // indicates not defined continue; } if (Qnil == c->clas) { c->clas = path2class(c->name); } if (clas == c->clas && c->active) { c->encode(obj, depth, out); return true; } } return false; } VALUE oj_code_load(Code codes, VALUE clas, VALUE args) { Code c = codes; for (; NULL != c->name; c++) { if (Qundef == c->clas) { // indicates not defined continue; } if (Qnil == c->clas) { c->clas = path2class(c->name); } if (clas == c->clas) { if (NULL == c->decode) { break; } return c->decode(clas, args); } } return Qnil; } void oj_code_set_active(Code codes, VALUE clas, bool active) { Code c = codes; for (; NULL != c->name; c++) { if (Qundef == c->clas) { // indicates not defined continue; } if (Qnil == c->clas) { c->clas = path2class(c->name); } if (clas == c->clas || Qnil == clas) { c->active = active; if (Qnil != clas) { break; } } } } bool oj_code_has(Code codes, VALUE clas, bool encode) { Code c = codes; for (; NULL != c->name; c++) { if (Qundef == c->clas) { // indicates not defined continue; } if (Qnil == c->clas) { c->clas = path2class(c->name); } if (clas == c->clas) { if (encode) { return c->active && NULL != c->encode; } else { return c->active && NULL != c->decode; } } } return false; } void oj_code_attrs(VALUE obj, Attr attrs, int depth, Out out, bool with_class) { int d2 = depth + 1; int d3 = d2 + 1; size_t sep_len = out->opts->dump_opts.before_size + out->opts->dump_opts.after_size + 2; const char *classname = rb_obj_classname(obj); size_t len = strlen(classname); size_t size = d2 * out->indent + 10 + len + out->opts->create_id_len + sep_len; bool no_comma = true; assure_size(out, size); *out->cur++ = '{'; if (with_class) { fill_indent(out, d2); *out->cur++ = '"'; APPEND_CHARS(out->cur, out->opts->create_id, out->opts->create_id_len); *out->cur++ = '"'; if (0 < out->opts->dump_opts.before_size) { APPEND_CHARS(out->cur, out->opts->dump_opts.before_sep, out->opts->dump_opts.before_size); } *out->cur++ = ':'; if (0 < out->opts->dump_opts.after_size) { APPEND_CHARS(out->cur, out->opts->dump_opts.after_sep, out->opts->dump_opts.after_size); } *out->cur++ = '"'; APPEND_CHARS(out->cur, classname, len); *out->cur++ = '"'; no_comma = false; } size = d3 * out->indent + 2; for (; NULL != attrs->name; attrs++) { assure_size(out, size + attrs->len + sep_len + 2); if (no_comma) { no_comma = false; } else { *out->cur++ = ','; } fill_indent(out, d2); *out->cur++ = '"'; APPEND_CHARS(out->cur, attrs->name, attrs->len); *out->cur++ = '"'; if (0 < out->opts->dump_opts.before_size) { APPEND_CHARS(out->cur, out->opts->dump_opts.before_sep, out->opts->dump_opts.before_size); } *out->cur++ = ':'; if (0 < out->opts->dump_opts.after_size) { APPEND_CHARS(out->cur, out->opts->dump_opts.after_sep, out->opts->dump_opts.after_size); } if (Qundef == attrs->value) { if (Qundef != attrs->time) { switch (out->opts->time_format) { case RubyTime: oj_dump_ruby_time(attrs->time, out); break; case XmlTime: oj_dump_xml_time(attrs->time, out); break; case UnixZTime: oj_dump_time(attrs->time, out, true); break; case UnixTime: default: oj_dump_time(attrs->time, out, false); break; } } else { char buf[32]; char *b = buf + sizeof(buf) - 1; bool neg = false; long num = attrs->num; size_t cnt = 0; if (0 > num) { neg = true; num = -num; } *b-- = '\0'; if (0 < num) { b = oj_longlong_to_string(num, neg, b); } else { *b = '0'; } cnt = sizeof(buf) - (b - buf) - 1; assure_size(out, cnt); APPEND_CHARS(out->cur, b, cnt); } } else { oj_dump_compat_val(attrs->value, d3, out, true); } } assure_size(out, depth * out->indent + 2); fill_indent(out, depth); *out->cur++ = '}'; *out->cur = '\0'; }