/* @overload rindex(pattern, offset = -1) * * Returns the maximal index of the receiver where PATTERN matches, equal to * or less than _i_, where _i_ = OFFSET if OFFSET ≥ 0, _i_ = {#length} - * abs(OFFSET) otherwise, or nil if there is no match. * * If PATTERN is a Regexp, the Regexp special variables `$&`, `$'`, * <code>$\`</code>, `$1`, `$2`, …, `$`_n_ are updated accordingly. * * If PATTERN responds to `#to_str`, the matching is performed by a byte * comparison. * * @param [Regexp, #to_str] pattern * @param [#to_int] offset * @return [Integer, nil] * @see #index */ VALUE rb_u_string_rindex_m(int argc, VALUE *argv, VALUE self) { const struct rb_u_string *string = RVAL2USTRING(self); VALUE sub, rboffset; long offset; if (rb_scan_args(argc, argv, "11", &sub, &rboffset) == 2) offset = NUM2LONG(rboffset); else /* TODO: Why not simply use -1? Benchmark which is faster. */ offset = u_n_chars_n(USTRING_STR(string), USTRING_LENGTH(string)); const char *begin = rb_u_string_begin_from_offset(string, offset); const char *end = USTRING_END(string); if (begin == NULL) { if (offset <= 0) { if (TYPE(sub) == T_REGEXP) rb_backref_set(Qnil); return Qnil; } begin = end; /* TODO: this converting back and forward can be optimized away * if rb_u_string_index_regexp() and rb_u_string_rindex() were split up * into two additional functions, adding * rb_u_string_index_regexp_pointer() and rb_u_string_rindex_pointer(), * so that one can pass a pointer to start at immediately * instead of an offset that gets calculated into a pointer. */ offset = u_n_chars_n(USTRING_STR(string), USTRING_LENGTH(string)); } switch (TYPE(sub)) { case T_REGEXP: /* TODO: What’s this first test for, exactly? */ if (RREGEXP(sub)->ptr == NULL || RREGEXP_SRC_LEN(sub) > 0) offset = rb_u_string_index_regexp(self, begin, sub, true); break; default: { VALUE tmp = rb_check_string_type(sub); if (NIL_P(tmp)) rb_u_raise(rb_eTypeError, "type mismatch: %s given", rb_obj_classname(sub)); sub = tmp; } /* fall through */ case T_STRING: offset = rb_u_string_rindex(self, sub, offset); break; } if (offset < 0) return Qnil; return LONG2NUM(offset); }
static int write_element_allow_id(VALUE key, VALUE value, VALUE extra, int allow_id) { bson_buffer* buffer = (bson_buffer*)NUM2INT(rb_ary_entry(extra, 0)); VALUE check_keys = rb_ary_entry(extra, 1); if (TYPE(key) == T_SYMBOL) { // TODO better way to do this... ? key = rb_str_new2(rb_id2name(SYM2ID(key))); } if (TYPE(key) != T_STRING) { rb_raise(rb_eTypeError, "keys must be strings or symbols"); } if (!allow_id && strcmp("_id", RSTRING_PTR(key)) == 0) { return ST_CONTINUE; } if (check_keys == Qtrue) { if (RSTRING_LEN(key) > 0 && RSTRING_PTR(key)[0] == '$') { rb_raise(rb_eRuntimeError, "key must not start with '$'"); } int i; for (i = 0; i < RSTRING_LEN(key); i++) { if (RSTRING_PTR(key)[i] == '.') { rb_raise(rb_eRuntimeError, "key must not contain '.'"); } } } switch(TYPE(value)) { case T_BIGNUM: { if (rb_funcall(value, rb_intern(">"), 1, INT2NUM(2147483647)) == Qtrue || rb_funcall(value, rb_intern("<"), 1, INT2NUM(-2147483648)) == Qtrue) { rb_raise(rb_eRangeError, "MongoDB can only handle 4-byte ints - try converting to a double before saving"); } write_name_and_type(buffer, key, 0x10); VALUE as_f = rb_funcall(value, rb_intern("to_f"), 0); int int_value = NUM2LL(as_f); buffer_write_bytes(buffer, (char*)&int_value, 4); break; } case T_FIXNUM: { write_name_and_type(buffer, key, 0x10); int int_value = FIX2INT(value); buffer_write_bytes(buffer, (char*)&int_value, 4); break; } case T_TRUE: { write_name_and_type(buffer, key, 0x08); buffer_write_bytes(buffer, &one, 1); break; } case T_FALSE: { write_name_and_type(buffer, key, 0x08); buffer_write_bytes(buffer, &zero, 1); break; } case T_FLOAT: { write_name_and_type(buffer, key, 0x01); double d = NUM2DBL(value); buffer_write_bytes(buffer, (char*)&d, 8); break; } case T_NIL: { write_name_and_type(buffer, key, 0x0A); break; } case T_HASH: { write_name_and_type(buffer, key, 0x03); write_doc(buffer, value, check_keys); break; } case T_ARRAY: { write_name_and_type(buffer, key, 0x04); int start_position = buffer->position; // save space for length int length_location = buffer_save_bytes(buffer, 4); int items = RARRAY_LEN(value); VALUE* values = RARRAY_PTR(value); int i; for(i = 0; i < items; i++) { char* name; asprintf(&name, "%d", i); VALUE key = rb_str_new2(name); write_element(key, values[i], pack_extra(buffer, check_keys)); free(name); } // write null byte and fill in length buffer_write_bytes(buffer, &zero, 1); int obj_length = buffer->position - start_position; memcpy(buffer->buffer + length_location, &obj_length, 4); break; } case T_STRING: { if (strcmp(rb_class2name(RBASIC(value)->klass), "XGen::Mongo::Driver::Code") == 0) { write_name_and_type(buffer, key, 0x0F); int start_position = buffer->position; int length_location = buffer_save_bytes(buffer, 4); int length = RSTRING_LEN(value) + 1; buffer_write_bytes(buffer, (char*)&length, 4); buffer_write_bytes(buffer, RSTRING_PTR(value), length - 1); buffer_write_bytes(buffer, &zero, 1); write_doc(buffer, rb_funcall(value, rb_intern("scope"), 0), Qfalse); int total_length = buffer->position - start_position; memcpy(buffer->buffer + length_location, &total_length, 4); break; } else { write_name_and_type(buffer, key, 0x02); int length = RSTRING_LEN(value) + 1; buffer_write_bytes(buffer, (char*)&length, 4); buffer_write_bytes(buffer, RSTRING_PTR(value), length - 1); buffer_write_bytes(buffer, &zero, 1); break; } } case T_SYMBOL: { write_name_and_type(buffer, key, 0x0E); const char* str_value = rb_id2name(SYM2ID(value)); int length = strlen(str_value) + 1; buffer_write_bytes(buffer, (char*)&length, 4); buffer_write_bytes(buffer, str_value, length); break; } case T_OBJECT: { // TODO there has to be a better way to do these checks... const char* cls = rb_class2name(RBASIC(value)->klass); if (strcmp(cls, "XGen::Mongo::Driver::Binary") == 0 || strcmp(cls, "ByteBuffer") == 0) { write_name_and_type(buffer, key, 0x05); const char subtype = strcmp(cls, "ByteBuffer") ? (const char)FIX2INT(rb_funcall(value, rb_intern("subtype"), 0)) : 2; VALUE string_data = rb_funcall(value, rb_intern("to_s"), 0); int length = RSTRING_LEN(string_data); if (subtype == 2) { const int other_length = length + 4; buffer_write_bytes(buffer, (const char*)&other_length, 4); buffer_write_bytes(buffer, &subtype, 1); } buffer_write_bytes(buffer, (const char*)&length, 4); if (subtype != 2) { buffer_write_bytes(buffer, &subtype, 1); } buffer_write_bytes(buffer, RSTRING_PTR(string_data), length); break; } if (strcmp(cls, "XGen::Mongo::Driver::ObjectID") == 0) { write_name_and_type(buffer, key, 0x07); VALUE as_array = rb_funcall(value, rb_intern("to_a"), 0); int i; for (i = 0; i < 12; i++) { char byte = (char)FIX2INT(RARRAY_PTR(as_array)[i]); buffer_write_bytes(buffer, &byte, 1); } break; } if (strcmp(cls, "XGen::Mongo::Driver::DBRef") == 0) { write_name_and_type(buffer, key, 0x03); int start_position = buffer->position; // save space for length int length_location = buffer_save_bytes(buffer, 4); VALUE ns = rb_funcall(value, rb_intern("namespace"), 0); write_element(rb_str_new2("$ref"), ns, pack_extra(buffer, Qfalse)); VALUE oid = rb_funcall(value, rb_intern("object_id"), 0); write_element(rb_str_new2("$id"), oid, pack_extra(buffer, Qfalse)); // write null byte and fill in length buffer_write_bytes(buffer, &zero, 1); int obj_length = buffer->position - start_position; memcpy(buffer->buffer + length_location, &obj_length, 4); break; } if (strcmp(cls, "XGen::Mongo::Driver::Undefined") == 0) { write_name_and_type(buffer, key, 0x06); break; } } case T_DATA: { // TODO again, is this really the only way to do this? const char* cls = rb_class2name(RBASIC(value)->klass); if (strcmp(cls, "Time") == 0) { write_name_and_type(buffer, key, 0x09); double t = NUM2DBL(rb_funcall(value, rb_intern("to_f"), 0)); long long time_since_epoch = (long long)round(t * 1000); buffer_write_bytes(buffer, (const char*)&time_since_epoch, 8); break; } } case T_REGEXP: { write_name_and_type(buffer, key, 0x0B); int length = RREGEXP_SRC_LEN(value); char* pattern = (char*)RREGEXP_SRC_PTR(value); buffer_write_bytes(buffer, pattern, length); buffer_write_bytes(buffer, &zero, 1); long flags = RREGEXP(value)->ptr->options; if (flags & IGNORECASE) { char ignorecase = 'i'; buffer_write_bytes(buffer, &ignorecase, 1); } if (flags & MULTILINE) { char multiline = 'm'; buffer_write_bytes(buffer, &multiline, 1); } if (flags & EXTENDED) { char extended = 'x'; buffer_write_bytes(buffer, &extended, 1); } VALUE has_extra = rb_funcall(value, rb_intern("respond_to?"), 1, rb_str_new2("extra_options_str")); if (TYPE(has_extra) == T_TRUE) { VALUE extra = rb_funcall(value, rb_intern("extra_options_str"), 0); int old_position = buffer->position; buffer_write_bytes(buffer, RSTRING_PTR(extra), RSTRING_LEN(extra)); qsort(buffer->buffer + old_position, RSTRING_LEN(extra), sizeof(char), cmp_char); } buffer_write_bytes(buffer, &zero, 1); break; } default: { rb_raise(rb_eTypeError, "no c encoder for this type yet (%d)", TYPE(value)); break; } } return ST_CONTINUE; }
static size_t memsize_of(VALUE obj) { size_t size = 0; if (SPECIAL_CONST_P(obj)) { return 0; } if (FL_TEST(obj, FL_EXIVAR)) { size += rb_generic_ivar_memsize(obj); } switch (BUILTIN_TYPE(obj)) { case T_OBJECT: if (!(RBASIC(obj)->flags & ROBJECT_EMBED) && ROBJECT(obj)->as.heap.ivptr) { size += ROBJECT(obj)->as.heap.numiv * sizeof(VALUE); } break; case T_MODULE: case T_CLASS: size += st_memsize(RCLASS_M_TBL(obj)); if (RCLASS_IV_TBL(obj)) { size += st_memsize(RCLASS_IV_TBL(obj)); } if (RCLASS_IV_INDEX_TBL(obj)) { size += st_memsize(RCLASS_IV_INDEX_TBL(obj)); } if (RCLASS(obj)->ptr->iv_tbl) { size += st_memsize(RCLASS(obj)->ptr->iv_tbl); } if (RCLASS(obj)->ptr->const_tbl) { size += st_memsize(RCLASS(obj)->ptr->const_tbl); } size += sizeof(rb_classext_t); break; case T_STRING: size += rb_str_memsize(obj); break; case T_ARRAY: size += rb_ary_memsize(obj); break; case T_HASH: if (RHASH(obj)->ntbl) { size += st_memsize(RHASH(obj)->ntbl); } break; case T_REGEXP: if (RREGEXP(obj)->ptr) { size += onig_memsize(RREGEXP(obj)->ptr); } break; case T_DATA: size += rb_objspace_data_type_memsize(obj); break; case T_MATCH: if (RMATCH(obj)->rmatch) { struct rmatch *rm = RMATCH(obj)->rmatch; size += sizeof(struct re_registers); /* TODO: onig_region_memsize(&rm->regs); */ size += sizeof(struct rmatch_offset) * rm->char_offset_num_allocated; size += sizeof(struct rmatch); } break; case T_FILE: if (RFILE(obj)->fptr) { size += rb_io_memsize(RFILE(obj)->fptr); } break; case T_RATIONAL: case T_COMPLEX: break; case T_ICLASS: /* iClass shares table with the module */ break; case T_FLOAT: break; case T_BIGNUM: if (!(RBASIC(obj)->flags & RBIGNUM_EMBED_FLAG) && RBIGNUM_DIGITS(obj)) { size += RBIGNUM_LEN(obj) * sizeof(BDIGIT); } break; case T_NODE: switch (nd_type(obj)) { case NODE_SCOPE: if (RNODE(obj)->u1.tbl) { /* TODO: xfree(RANY(obj)->as.node.u1.tbl); */ } break; case NODE_ALLOCA: /* TODO: xfree(RANY(obj)->as.node.u1.node); */ ; } break; /* no need to free iv_tbl */ case T_STRUCT: if ((RBASIC(obj)->flags & RSTRUCT_EMBED_LEN_MASK) == 0 && RSTRUCT(obj)->as.heap.ptr) { size += sizeof(VALUE) * RSTRUCT_LEN(obj); } break; case T_ZOMBIE: break; default: rb_bug("objspace/memsize_of(): unknown data type 0x%x(%p)", BUILTIN_TYPE(obj), (void*)obj); } return size; }
static int write_element_allow_id(VALUE key, VALUE value, VALUE extra, int allow_id) { buffer_t buffer = (buffer_t)NUM2LL(rb_ary_entry(extra, 0)); VALUE check_keys = rb_ary_entry(extra, 1); if (TYPE(key) == T_SYMBOL) { // TODO better way to do this... ? key = rb_str_new2(rb_id2name(SYM2ID(key))); } if (TYPE(key) != T_STRING) { buffer_free(buffer); rb_raise(rb_eTypeError, "keys must be strings or symbols"); } if (!allow_id && strcmp("_id", RSTRING_PTR(key)) == 0) { return ST_CONTINUE; } if (check_keys == Qtrue) { int i; if (RSTRING_LEN(key) > 0 && RSTRING_PTR(key)[0] == '$') { buffer_free(buffer); rb_raise(InvalidName, "key must not start with '$'"); } for (i = 0; i < RSTRING_LEN(key); i++) { if (RSTRING_PTR(key)[i] == '.') { buffer_free(buffer); rb_raise(InvalidName, "key must not contain '.'"); } } } switch(TYPE(value)) { case T_BIGNUM: case T_FIXNUM: { if (rb_funcall(value, rb_intern(">"), 1, LL2NUM(9223372036854775807LL)) == Qtrue || rb_funcall(value, rb_intern("<"), 1, LL2NUM(-9223372036854775808ULL)) == Qtrue) { buffer_free(buffer); rb_raise(rb_eRangeError, "MongoDB can only handle 8-byte ints"); } if (rb_funcall(value, rb_intern(">"), 1, INT2NUM(2147483647L)) == Qtrue || rb_funcall(value, rb_intern("<"), 1, INT2NUM(-2147483648L)) == Qtrue) { long long ll_value; write_name_and_type(buffer, key, 0x12); ll_value = NUM2LL(value); SAFE_WRITE(buffer, (char*)&ll_value, 8); } else { int int_value; write_name_and_type(buffer, key, 0x10); int_value = NUM2LL(value); SAFE_WRITE(buffer, (char*)&int_value, 4); } break; } case T_TRUE: { write_name_and_type(buffer, key, 0x08); SAFE_WRITE(buffer, &one, 1); break; } case T_FALSE: { write_name_and_type(buffer, key, 0x08); SAFE_WRITE(buffer, &zero, 1); break; } case T_FLOAT: { double d = NUM2DBL(value); write_name_and_type(buffer, key, 0x01); SAFE_WRITE(buffer, (char*)&d, 8); break; } case T_NIL: { write_name_and_type(buffer, key, 0x0A); break; } case T_HASH: { write_name_and_type(buffer, key, 0x03); write_doc(buffer, value, check_keys); break; } case T_ARRAY: { buffer_position length_location, start_position, obj_length; int items, i; VALUE* values; write_name_and_type(buffer, key, 0x04); start_position = buffer_get_position(buffer); // save space for length length_location = buffer_save_space(buffer, 4); if (length_location == -1) { rb_raise(rb_eNoMemError, "failed to allocate memory in buffer.c"); } items = RARRAY_LEN(value); values = RARRAY_PTR(value); for(i = 0; i < items; i++) { char* name; VALUE key; INT2STRING(&name, i); key = rb_str_new2(name); write_element(key, values[i], pack_extra(buffer, check_keys)); free(name); } // write null byte and fill in length SAFE_WRITE(buffer, &zero, 1); obj_length = buffer_get_position(buffer) - start_position; SAFE_WRITE_AT_POS(buffer, length_location, (const char*)&obj_length, 4); break; } case T_STRING: { if (strcmp(rb_class2name(RBASIC(value)->klass), "Mongo::Code") == 0) { buffer_position length_location, start_position, total_length; int length; write_name_and_type(buffer, key, 0x0F); start_position = buffer_get_position(buffer); length_location = buffer_save_space(buffer, 4); if (length_location == -1) { rb_raise(rb_eNoMemError, "failed to allocate memory in buffer.c"); } length = RSTRING_LEN(value) + 1; SAFE_WRITE(buffer, (char*)&length, 4); SAFE_WRITE(buffer, RSTRING_PTR(value), length - 1); SAFE_WRITE(buffer, &zero, 1); write_doc(buffer, rb_funcall(value, rb_intern("scope"), 0), Qfalse); total_length = buffer_get_position(buffer) - start_position; SAFE_WRITE_AT_POS(buffer, length_location, (const char*)&total_length, 4); break; } else { int length; write_name_and_type(buffer, key, 0x02); value = TO_UTF8(value); length = RSTRING_LEN(value) + 1; SAFE_WRITE(buffer, (char*)&length, 4); write_utf8(buffer, value, 0); SAFE_WRITE(buffer, &zero, 1); break; } } case T_SYMBOL: { const char* str_value = rb_id2name(SYM2ID(value)); int length = strlen(str_value) + 1; write_name_and_type(buffer, key, 0x0E); SAFE_WRITE(buffer, (char*)&length, 4); SAFE_WRITE(buffer, str_value, length); break; } case T_OBJECT: { // TODO there has to be a better way to do these checks... const char* cls = rb_class2name(RBASIC(value)->klass); if (strcmp(cls, "Mongo::Binary") == 0 || strcmp(cls, "ByteBuffer") == 0) { const char subtype = strcmp(cls, "ByteBuffer") ? (const char)FIX2INT(rb_funcall(value, rb_intern("subtype"), 0)) : 2; VALUE string_data = rb_funcall(value, rb_intern("to_s"), 0); int length = RSTRING_LEN(string_data); write_name_and_type(buffer, key, 0x05); if (subtype == 2) { const int other_length = length + 4; SAFE_WRITE(buffer, (const char*)&other_length, 4); SAFE_WRITE(buffer, &subtype, 1); } SAFE_WRITE(buffer, (const char*)&length, 4); if (subtype != 2) { SAFE_WRITE(buffer, &subtype, 1); } SAFE_WRITE(buffer, RSTRING_PTR(string_data), length); break; } if (strcmp(cls, "Mongo::ObjectID") == 0) { VALUE as_array = rb_funcall(value, rb_intern("to_a"), 0); int i; write_name_and_type(buffer, key, 0x07); for (i = 0; i < 12; i++) { char byte = (char)FIX2INT(RARRAY_PTR(as_array)[i]); SAFE_WRITE(buffer, &byte, 1); } break; } if (strcmp(cls, "Mongo::DBRef") == 0) { buffer_position length_location, start_position, obj_length; VALUE ns, oid; write_name_and_type(buffer, key, 0x03); start_position = buffer_get_position(buffer); // save space for length length_location = buffer_save_space(buffer, 4); if (length_location == -1) { rb_raise(rb_eNoMemError, "failed to allocate memory in buffer.c"); } ns = rb_funcall(value, rb_intern("namespace"), 0); write_element(rb_str_new2("$ref"), ns, pack_extra(buffer, Qfalse)); oid = rb_funcall(value, rb_intern("object_id"), 0); write_element(rb_str_new2("$id"), oid, pack_extra(buffer, Qfalse)); // write null byte and fill in length SAFE_WRITE(buffer, &zero, 1); obj_length = buffer_get_position(buffer) - start_position; SAFE_WRITE_AT_POS(buffer, length_location, (const char*)&obj_length, 4); break; } if (strcmp(cls, "Mongo::MaxKey") == 0) { write_name_and_type(buffer, key, 0x7f); break; } if (strcmp(cls, "Mongo::MinKey") == 0) { write_name_and_type(buffer, key, 0xff); break; } if (strcmp(cls, "DateTime") == 0 || strcmp(cls, "Date") == 0 || strcmp(cls, "ActiveSupport::TimeWithZone") == 0) { buffer_free(buffer); rb_raise(InvalidDocument, "%s is not currently supported; use a UTC Time instance instead.", cls); break; } if(strcmp(cls, "Complex") == 0 || strcmp(cls, "Rational") == 0 || strcmp(cls, "BigDecimal") == 0) { buffer_free(buffer); rb_raise(InvalidDocument, "Cannot serialize the Numeric type %s as BSON; only Bignum, Fixnum, and Float are supported.", cls); break; } buffer_free(buffer); rb_raise(InvalidDocument, "Cannot serialize an object of class %s into BSON.", cls); break; } case T_DATA: { const char* cls = rb_class2name(RBASIC(value)->klass); if (strcmp(cls, "Time") == 0) { double t = NUM2DBL(rb_funcall(value, rb_intern("to_f"), 0)); long long time_since_epoch = (long long)round(t * 1000); write_name_and_type(buffer, key, 0x09); SAFE_WRITE(buffer, (const char*)&time_since_epoch, 8); break; } if(strcmp(cls, "BigDecimal") == 0) { buffer_free(buffer); rb_raise(InvalidDocument, "Cannot serialize the Numeric type %s as BSON; only Bignum, Fixnum, and Float are supported.", cls); break; } buffer_free(buffer); rb_raise(InvalidDocument, "Cannot serialize an object of class %s into BSON.", cls); break; } case T_REGEXP: { VALUE pattern = RREGEXP_SRC(value); long flags = RREGEXP(value)->ptr->options; VALUE has_extra; write_name_and_type(buffer, key, 0x0B); pattern = TO_UTF8(pattern); write_utf8(buffer, pattern, 1); SAFE_WRITE(buffer, &zero, 1); if (flags & IGNORECASE) { char ignorecase = 'i'; SAFE_WRITE(buffer, &ignorecase, 1); } if (flags & MULTILINE) { char multiline = 'm'; SAFE_WRITE(buffer, &multiline, 1); } if (flags & EXTENDED) { char extended = 'x'; SAFE_WRITE(buffer, &extended, 1); } has_extra = rb_funcall(value, rb_intern("respond_to?"), 1, rb_str_new2("extra_options_str")); if (TYPE(has_extra) == T_TRUE) { VALUE extra = rb_funcall(value, rb_intern("extra_options_str"), 0); buffer_position old_position = buffer_get_position(buffer); SAFE_WRITE(buffer, RSTRING_PTR(extra), RSTRING_LEN(extra)); qsort(buffer_get_buffer(buffer) + old_position, RSTRING_LEN(extra), sizeof(char), cmp_char); } SAFE_WRITE(buffer, &zero, 1); break; } default: { const char* cls = rb_class2name(RBASIC(value)->klass); buffer_free(buffer); rb_raise(InvalidDocument, "Cannot serialize an object of class %s (type %d) into BSON.", cls, TYPE(value)); break; } } return ST_CONTINUE; }
void obj_dump(VALUE obj, yajl_gen gen) { int type; yajl_gen_map_open(gen); yajl_gen_cstr(gen, "_id"); yajl_gen_value(gen, obj); struct obj_track *tracker = NULL; if (st_lookup(objs, (st_data_t)obj, (st_data_t *)&tracker) && BUILTIN_TYPE(obj) != T_NODE) { yajl_gen_cstr(gen, "file"); yajl_gen_cstr(gen, tracker->source); yajl_gen_cstr(gen, "line"); yajl_gen_integer(gen, tracker->line); } yajl_gen_cstr(gen, "type"); switch (type=BUILTIN_TYPE(obj)) { case T_DATA: yajl_gen_cstr(gen, "data"); if (RBASIC(obj)->klass) { yajl_gen_cstr(gen, "class"); yajl_gen_value(gen, RBASIC(obj)->klass); yajl_gen_cstr(gen, "class_name"); VALUE name = rb_classname(RBASIC(obj)->klass); if (RTEST(name)) yajl_gen_cstr(gen, RSTRING(name)->ptr); else yajl_gen_cstr(gen, 0); } break; case T_FILE: yajl_gen_cstr(gen, "file"); break; case T_FLOAT: yajl_gen_cstr(gen, "float"); yajl_gen_cstr(gen, "data"); yajl_gen_double(gen, RFLOAT(obj)->value); break; case T_BIGNUM: yajl_gen_cstr(gen, "bignum"); yajl_gen_cstr(gen, "negative"); yajl_gen_bool(gen, RBIGNUM(obj)->sign == 0); yajl_gen_cstr(gen, "length"); yajl_gen_integer(gen, RBIGNUM(obj)->len); yajl_gen_cstr(gen, "data"); yajl_gen_string(gen, RBIGNUM(obj)->digits, RBIGNUM(obj)->len); break; case T_MATCH: yajl_gen_cstr(gen, "match"); yajl_gen_cstr(gen, "data"); yajl_gen_value(gen, RMATCH(obj)->str); break; case T_REGEXP: yajl_gen_cstr(gen, "regexp"); yajl_gen_cstr(gen, "length"); yajl_gen_integer(gen, RREGEXP(obj)->len); yajl_gen_cstr(gen, "data"); yajl_gen_cstr(gen, RREGEXP(obj)->str); break; case T_SCOPE: yajl_gen_cstr(gen, "scope"); struct SCOPE *scope = (struct SCOPE *)obj; if (scope->local_tbl) { int i = 1; int n = scope->local_tbl[0]; VALUE *list = &scope->local_vars[-1]; VALUE cur = *list++; yajl_gen_cstr(gen, "node"); yajl_gen_value(gen, cur); if (n) { yajl_gen_cstr(gen, "variables"); yajl_gen_map_open(gen); while (n--) { cur = *list++; yajl_gen_cstr(gen, scope->local_tbl[i] == 95 ? "_" : rb_id2name(scope->local_tbl[i])); yajl_gen_value(gen, cur); i++; } yajl_gen_map_close(gen); } } break; case T_NODE: yajl_gen_cstr(gen, "node"); yajl_gen_cstr(gen, "node_type"); yajl_gen_cstr(gen, nd_type_str(obj)); yajl_gen_cstr(gen, "file"); yajl_gen_cstr(gen, RNODE(obj)->nd_file); yajl_gen_cstr(gen, "line"); yajl_gen_integer(gen, nd_line(obj)); yajl_gen_cstr(gen, "node_code"); yajl_gen_integer(gen, nd_type(obj)); switch (nd_type(obj)) { case NODE_SCOPE: break; } break; case T_STRING: yajl_gen_cstr(gen, "string"); yajl_gen_cstr(gen, "length"); yajl_gen_integer(gen, RSTRING(obj)->len); if (FL_TEST(obj, ELTS_SHARED|FL_USER3)) { yajl_gen_cstr(gen, "shared"); yajl_gen_value(gen, RSTRING(obj)->aux.shared); yajl_gen_cstr(gen, "flags"); yajl_gen_array_open(gen); if (FL_TEST(obj, ELTS_SHARED)) yajl_gen_cstr(gen, "elts_shared"); if (FL_TEST(obj, FL_USER3)) yajl_gen_cstr(gen, "str_assoc"); yajl_gen_array_close(gen); } else { yajl_gen_cstr(gen, "data"); yajl_gen_string(gen, (unsigned char *)RSTRING(obj)->ptr, RSTRING(obj)->len); } break; case T_VARMAP: yajl_gen_cstr(gen, "varmap"); struct RVarmap *vars = (struct RVarmap *)obj; if (vars->next) { yajl_gen_cstr(gen, "next"); yajl_gen_value(gen, (VALUE)vars->next); } if (vars->id) { yajl_gen_cstr(gen, "data"); yajl_gen_map_open(gen); yajl_gen_cstr(gen, rb_id2name(vars->id)); yajl_gen_value(gen, vars->val); yajl_gen_map_close(gen); } break; case T_CLASS: case T_MODULE: case T_ICLASS: yajl_gen_cstr(gen, type==T_CLASS ? "class" : type==T_MODULE ? "module" : "iclass"); yajl_gen_cstr(gen, "name"); VALUE name = rb_classname(obj); if (RTEST(name)) yajl_gen_cstr(gen, RSTRING(name)->ptr); else yajl_gen_cstr(gen, 0); yajl_gen_cstr(gen, "super"); yajl_gen_value(gen, RCLASS(obj)->super); yajl_gen_cstr(gen, "super_name"); VALUE super_name = rb_classname(RCLASS(obj)->super); if (RTEST(super_name)) yajl_gen_cstr(gen, RSTRING(super_name)->ptr); else yajl_gen_cstr(gen, 0); if (FL_TEST(obj, FL_SINGLETON)) { yajl_gen_cstr(gen, "singleton"); yajl_gen_bool(gen, 1); } if (RCLASS(obj)->iv_tbl && RCLASS(obj)->iv_tbl->num_entries) { yajl_gen_cstr(gen, "ivars"); yajl_gen_map_open(gen); st_foreach(RCLASS(obj)->iv_tbl, each_ivar, (st_data_t)gen); yajl_gen_map_close(gen); } if (type != T_ICLASS && RCLASS(obj)->m_tbl && RCLASS(obj)->m_tbl->num_entries) { yajl_gen_cstr(gen, "methods"); yajl_gen_map_open(gen); st_foreach(RCLASS(obj)->m_tbl, each_ivar, (st_data_t)gen); yajl_gen_map_close(gen); } break; case T_OBJECT: yajl_gen_cstr(gen, "object"); yajl_gen_cstr(gen, "class"); yajl_gen_value(gen, RBASIC(obj)->klass); yajl_gen_cstr(gen, "class_name"); yajl_gen_cstr(gen, rb_obj_classname(obj)); struct RClass *klass = RCLASS(obj); if (klass->iv_tbl && klass->iv_tbl->num_entries) { yajl_gen_cstr(gen, "ivars"); yajl_gen_map_open(gen); st_foreach(klass->iv_tbl, each_ivar, (st_data_t)gen); yajl_gen_map_close(gen); } break; case T_ARRAY: yajl_gen_cstr(gen, "array"); struct RArray *ary = RARRAY(obj); yajl_gen_cstr(gen, "length"); yajl_gen_integer(gen, ary->len); if (FL_TEST(obj, ELTS_SHARED)) { yajl_gen_cstr(gen, "shared"); yajl_gen_value(gen, ary->aux.shared); } else if (ary->len) { yajl_gen_cstr(gen, "data"); yajl_gen_array_open(gen); int i; for(i=0; i < ary->len; i++) yajl_gen_value(gen, ary->ptr[i]); yajl_gen_array_close(gen); } break; case T_HASH: yajl_gen_cstr(gen, "hash"); struct RHash *hash = RHASH(obj); yajl_gen_cstr(gen, "length"); if (hash->tbl) yajl_gen_integer(gen, hash->tbl->num_entries); else yajl_gen_integer(gen, 0); yajl_gen_cstr(gen, "default"); yajl_gen_value(gen, hash->ifnone); if (hash->tbl && hash->tbl->num_entries) { yajl_gen_cstr(gen, "data"); //yajl_gen_map_open(gen); yajl_gen_array_open(gen); st_foreach(hash->tbl, each_hash_entry, (st_data_t)gen); yajl_gen_array_close(gen); //yajl_gen_map_close(gen); } break; default: yajl_gen_cstr(gen, "unknown"); } yajl_gen_cstr(gen, "code"); yajl_gen_integer(gen, BUILTIN_TYPE(obj)); yajl_gen_map_close(gen); }