Feature #11299 ยป 0001-use-Array-instead-of-custom-struct-for-generic-ivars.patch
array.c | ||
---|---|---|
#define ARY_SET(a, i, v) RARRAY_ASET((assert(!ARY_SHARED_P(a)), (a)), (i), (v))
|
||
void
|
||
rb_mem_clear(register VALUE *mem, register long size)
|
||
static inline void
|
||
memfill(register VALUE *mem, register long size, register VALUE val)
|
||
{
|
||
while (size--) {
|
||
*mem++ = Qnil;
|
||
*mem++ = val;
|
||
}
|
||
}
|
||
void
|
||
rb_mem_clear(register VALUE *mem, register long size)
|
||
{
|
||
memfill(mem, size, Qnil);
|
||
}
|
||
static void
|
||
ary_mem_clear(VALUE ary, long beg, long size)
|
||
{
|
||
... | ... | |
});
|
||
}
|
||
static inline void
|
||
memfill(register VALUE *mem, register long size, register VALUE val)
|
||
{
|
||
while (size--) {
|
||
*mem++ = val;
|
||
}
|
||
}
|
||
static void
|
||
ary_memfill(VALUE ary, long beg, long size, VALUE val)
|
||
{
|
||
... | ... | |
void
|
||
rb_ary_store(VALUE ary, long idx, VALUE val)
|
||
{
|
||
rb_ary_store_fill(ary, idx, val, Qnil);
|
||
}
|
||
void
|
||
rb_ary_store_fill(VALUE ary, long idx, VALUE val, VALUE fill)
|
||
{
|
||
long len = RARRAY_LEN(ary);
|
||
if (idx < 0) {
|
||
... | ... | |
ary_double_capa(ary, idx);
|
||
}
|
||
if (idx > len) {
|
||
ary_mem_clear(ary, len, idx - len + 1);
|
||
ary_memfill(ary, len, idx - len + 1, fill);
|
||
}
|
||
if (idx >= len) {
|
internal.h | ||
---|---|---|
void rb_ary_delete_same(VALUE, VALUE);
|
||
VALUE rb_ary_tmp_new_fill(long capa);
|
||
size_t rb_ary_memsize(VALUE);
|
||
void rb_ary_store_fill(VALUE ary, long idx, VALUE val, VALUE fill);
|
||
#ifdef __GNUC__
|
||
#define rb_ary_new_from_args(n, ...) \
|
||
__extension__ ({ \
|
variable.c | ||
---|---|---|
static st_table *generic_iv_tbl;
|
||
static st_table *generic_iv_tbl_compat;
|
||
/* per-object */
|
||
struct gen_ivtbl {
|
||
long numiv; /* only uses 32-bits */
|
||
VALUE ivptr[1]; /* flexible array */
|
||
};
|
||
struct ivar_update {
|
||
union {
|
||
st_table *iv_index_tbl;
|
||
struct gen_ivtbl *ivtbl;
|
||
VALUE ivtbl;
|
||
} u;
|
||
st_data_t index;
|
||
int extended;
|
||
... | ... | |
}
|
||
struct gen_ivar_compat_tbl {
|
||
struct gen_ivtbl *ivtbl;
|
||
VALUE ivtbl;
|
||
st_table *tbl;
|
||
};
|
||
... | ... | |
{
|
||
struct gen_ivar_compat_tbl *a = (struct gen_ivar_compat_tbl *)arg;
|
||
if ((long)index < a->ivtbl->numiv) {
|
||
VALUE val = a->ivtbl->ivptr[index];
|
||
if ((long)index < RARRAY_LEN(a->ivtbl)) {
|
||
VALUE val = RARRAY_AREF(a->ivtbl, index);
|
||
if (val != Qundef) {
|
||
st_add_direct(a->tbl, id, (st_data_t)val);
|
||
}
|
||
... | ... | |
}
|
||
static int
|
||
gen_ivtbl_get(VALUE obj, struct gen_ivtbl **ivtbl)
|
||
gen_ivtbl_get(VALUE obj, VALUE *ivtbl)
|
||
{
|
||
st_data_t data;
|
||
if (st_lookup(generic_iv_tbl, (st_data_t)obj, &data)) {
|
||
*ivtbl = (struct gen_ivtbl *)data;
|
||
*ivtbl = (VALUE)data;
|
||
return 1;
|
||
}
|
||
return 0;
|
||
... | ... | |
static VALUE
|
||
generic_ivar_delete(VALUE obj, ID id, VALUE undef)
|
||
{
|
||
struct gen_ivtbl *ivtbl;
|
||
VALUE ivtbl;
|
||
if (gen_ivtbl_get(obj, &ivtbl)) {
|
||
st_table *iv_index_tbl = RCLASS_IV_INDEX_TBL(rb_obj_class(obj));
|
||
st_data_t index;
|
||
if (st_lookup(iv_index_tbl, (st_data_t)id, &index)) {
|
||
if ((long)index < ivtbl->numiv) {
|
||
VALUE ret = ivtbl->ivptr[index];
|
||
if ((long)index < RARRAY_LEN(ivtbl)) {
|
||
VALUE ret = RARRAY_AREF(ivtbl, index);
|
||
ivtbl->ivptr[index] = Qundef;
|
||
RARRAY_ASET(ivtbl, index, Qundef);
|
||
return ret == Qundef ? undef : ret;
|
||
}
|
||
}
|
||
... | ... | |
static VALUE
|
||
generic_ivar_get(VALUE obj, ID id, VALUE undef)
|
||
{
|
||
struct gen_ivtbl *ivtbl;
|
||
VALUE ivtbl;
|
||
if (gen_ivtbl_get(obj, &ivtbl)) {
|
||
st_table *iv_index_tbl = RCLASS_IV_INDEX_TBL(rb_obj_class(obj));
|
||
st_data_t index;
|
||
if (st_lookup(iv_index_tbl, (st_data_t)id, &index)) {
|
||
if ((long)index < ivtbl->numiv) {
|
||
VALUE ret = ivtbl->ivptr[index];
|
||
if ((long)index < RARRAY_LEN(ivtbl)) {
|
||
VALUE ret = RARRAY_AREF(ivtbl, index);
|
||
return ret == Qundef ? undef : ret;
|
||
}
|
||
... | ... | |
return undef;
|
||
}
|
||
static size_t
|
||
gen_ivtbl_bytes(size_t n)
|
||
{
|
||
return sizeof(struct gen_ivtbl) + n * sizeof(VALUE) - sizeof(VALUE);
|
||
}
|
||
struct gen_ivtbl *
|
||
gen_ivtbl_resize(struct gen_ivtbl *old, long n)
|
||
{
|
||
long len = old ? old->numiv : 0;
|
||
struct gen_ivtbl *ivtbl = xrealloc(old, gen_ivtbl_bytes(n));
|
||
ivtbl->numiv = n;
|
||
for (; len < n; len++) {
|
||
ivtbl->ivptr[len] = Qundef;
|
||
}
|
||
return ivtbl;
|
||
}
|
||
struct gen_ivtbl *
|
||
gen_ivtbl_dup(const struct gen_ivtbl *orig)
|
||
{
|
||
size_t s = gen_ivtbl_bytes(orig->numiv);
|
||
struct gen_ivtbl *ivtbl = xmalloc(s);
|
||
memcpy(ivtbl, orig, s);
|
||
return ivtbl;
|
||
}
|
||
static long
|
||
iv_index_tbl_newsize(struct ivar_update *ivup)
|
||
{
|
||
... | ... | |
{
|
||
VALUE obj = (VALUE)*k;
|
||
struct ivar_update *ivup = (struct ivar_update *)u;
|
||
long newsize;
|
||
int ret = ST_CONTINUE;
|
||
struct gen_ivtbl *ivtbl;
|
||
if (existing) {
|
||
ivtbl = (struct gen_ivtbl *)*v;
|
||
if ((long)ivup->index >= ivtbl->numiv) {
|
||
goto resize;
|
||
}
|
||
ret = ST_STOP;
|
||
}
|
||
else {
|
||
FL_SET(obj, FL_EXIVAR);
|
||
ivtbl = 0;
|
||
resize:
|
||
newsize = iv_index_tbl_newsize(ivup);
|
||
ivtbl = gen_ivtbl_resize(ivtbl, newsize);
|
||
*v = (st_data_t)ivtbl;
|
||
ivup->u.ivtbl = (VALUE)*v;
|
||
return ST_STOP;
|
||
}
|
||
ivup->u.ivtbl = ivtbl;
|
||
return ret;
|
||
ivup->u.ivtbl = rb_ary_tmp_new(0);
|
||
FL_SET(obj, FL_EXIVAR);
|
||
*v = (st_data_t)ivup->u.ivtbl;
|
||
return ST_CONTINUE;
|
||
}
|
||
static VALUE
|
||
generic_ivar_defined(VALUE obj, ID id)
|
||
{
|
||
struct gen_ivtbl *ivtbl;
|
||
VALUE ivtbl;
|
||
st_table *iv_index_tbl = RCLASS_IV_INDEX_TBL(rb_obj_class(obj));
|
||
st_data_t index;
|
||
... | ... | |
if (!st_lookup(iv_index_tbl, (st_data_t)id, &index)) return Qfalse;
|
||
if (!gen_ivtbl_get(obj, &ivtbl)) return Qfalse;
|
||
if (((long)index < ivtbl->numiv) && (ivtbl->ivptr[index] != Qundef))
|
||
if ((long)index < RARRAY_LEN(ivtbl) && RARRAY_AREF(ivtbl, index) != Qundef)
|
||
return Qtrue;
|
||
return Qfalse;
|
||
... | ... | |
static int
|
||
generic_ivar_remove(VALUE obj, ID id, st_data_t *valp)
|
||
{
|
||
struct gen_ivtbl *ivtbl;
|
||
VALUE ivtbl;
|
||
st_data_t key = (st_data_t)id;
|
||
st_data_t index;
|
||
st_table *iv_index_tbl = RCLASS_IV_INDEX_TBL(rb_obj_class(obj));
|
||
... | ... | |
if (!st_lookup(iv_index_tbl, key, &index)) return 0;
|
||
if (!gen_ivtbl_get(obj, &ivtbl)) return 0;
|
||
if ((long)index < ivtbl->numiv) {
|
||
if (ivtbl->ivptr[index] != Qundef) {
|
||
ivtbl->ivptr[index] = Qundef;
|
||
if ((long)index < RARRAY_LEN(ivtbl)) {
|
||
if (RARRAY_AREF(ivtbl, index) != Qundef) {
|
||
RARRAY_ASET(ivtbl, index, Qundef);
|
||
return 1;
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
static void
|
||
gen_ivtbl_mark(const struct gen_ivtbl *ivtbl)
|
||
{
|
||
long i;
|
||
for (i = 0; i < ivtbl->numiv; i++) {
|
||
rb_gc_mark(ivtbl->ivptr[i]);
|
||
}
|
||
}
|
||
void
|
||
rb_mark_generic_ivar(VALUE obj)
|
||
{
|
||
struct gen_ivtbl *ivtbl;
|
||
VALUE ivtbl;
|
||
if (gen_ivtbl_get(obj, &ivtbl)) {
|
||
gen_ivtbl_mark(ivtbl);
|
||
rb_gc_mark(ivtbl);
|
||
}
|
||
}
|
||
... | ... | |
rb_free_generic_ivar(VALUE obj)
|
||
{
|
||
st_data_t key = (st_data_t)obj;
|
||
struct gen_ivtbl *ivtbl;
|
||
VALUE ivtbl;
|
||
if (st_delete(generic_iv_tbl, &key, (st_data_t *)&ivtbl))
|
||
xfree(ivtbl);
|
||
(void)st_delete(generic_iv_tbl, &key, (st_data_t *)&ivtbl);
|
||
/* rb_ary_clear is not suitable here since that can realloc, rely on GC */
|
||
if (generic_iv_tbl_compat) {
|
||
st_table *tbl;
|
||
... | ... | |
RUBY_FUNC_EXPORTED size_t
|
||
rb_generic_ivar_memsize(VALUE obj)
|
||
{
|
||
struct gen_ivtbl *ivtbl;
|
||
VALUE ivtbl;
|
||
if (gen_ivtbl_get(obj, &ivtbl))
|
||
return gen_ivtbl_bytes(ivtbl->numiv);
|
||
return rb_ary_memsize(ivtbl);
|
||
return 0;
|
||
}
|
||
static size_t
|
||
gen_ivtbl_count(const struct gen_ivtbl *ivtbl)
|
||
gen_ivtbl_count(const VALUE ivtbl)
|
||
{
|
||
long i;
|
||
size_t n = 0;
|
||
long len = RARRAY_LEN(ivtbl);
|
||
const VALUE *ptr = RARRAY_CONST_PTR(ivtbl);
|
||
for (i = 0; i < ivtbl->numiv; i++) {
|
||
if (ivtbl->ivptr[i] != Qundef) {
|
||
for (i = 0; i < len; i++) {
|
||
if (ptr[i] != Qundef) {
|
||
n++;
|
||
}
|
||
}
|
||
... | ... | |
st_update(generic_iv_tbl, (st_data_t)obj, generic_ivar_update,
|
||
(st_data_t)&ivup);
|
||
ivup.u.ivtbl->ivptr[ivup.index] = val;
|
||
rb_ary_store_fill(ivup.u.ivtbl, ivup.index, val, Qundef);
|
||
if (FL_ABLE(obj)) RB_OBJ_WRITTEN(obj, Qundef, val);
|
||
if (FL_ABLE(obj)) RB_OBJ_WRITTEN(obj, Qundef, ivup.u.ivtbl);
|
||
}
|
||
VALUE
|
||
... | ... | |
}
|
||
struct gen_ivar_tag {
|
||
struct gen_ivtbl *ivtbl;
|
||
VALUE ivtbl;
|
||
int (*func)(ID key, VALUE val, st_data_t arg);
|
||
st_data_t arg;
|
||
};
|
||
... | ... | |
{
|
||
struct gen_ivar_tag *arg = (struct gen_ivar_tag *)data;
|
||
if ((long)index < arg->ivtbl->numiv) {
|
||
VALUE val = arg->ivtbl->ivptr[index];
|
||
if ((long)index < RARRAY_LEN(arg->ivtbl)) {
|
||
VALUE val = RARRAY_AREF(arg->ivtbl, index);
|
||
if (val != Qundef) {
|
||
return (arg->func)((ID)key, val, arg->arg);
|
||
}
|
||
... | ... | |
struct givar_copy {
|
||
VALUE obj;
|
||
st_table *iv_index_tbl;
|
||
struct gen_ivtbl *ivtbl;
|
||
VALUE ivtbl;
|
||
};
|
||
static int
|
||
... | ... | |
ivup.extended = 0;
|
||
ivup.u.iv_index_tbl = c->iv_index_tbl;
|
||
iv_index_tbl_extend(&ivup, id);
|
||
if ((long)ivup.index >= c->ivtbl->numiv) {
|
||
size_t newsize = iv_index_tbl_newsize(&ivup);
|
||
c->ivtbl = gen_ivtbl_resize(c->ivtbl, newsize);
|
||
}
|
||
c->ivtbl->ivptr[ivup.index] = val;
|
||
rb_ary_store_fill(c->ivtbl, ivup.index, val, Qundef);
|
||
if (FL_ABLE(c->obj)) RB_OBJ_WRITTEN(c->obj, Qundef, val);
|
||
if (FL_ABLE(c->obj)) RB_OBJ_WRITTEN(c->obj, Qundef, c->ivtbl);
|
||
return ST_CONTINUE;
|
||
}
|
||
... | ... | |
void
|
||
rb_copy_generic_ivar(VALUE clone, VALUE obj)
|
||
{
|
||
struct gen_ivtbl *ivtbl;
|
||
VALUE ivtbl;
|
||
rb_check_frozen(clone);
|
||
... | ... | |
}
|
||
if (gen_ivtbl_get(obj, &ivtbl)) {
|
||
struct givar_copy c;
|
||
long i;
|
||
if (gen_ivtbl_count(ivtbl) == 0)
|
||
goto clear;
|
||
if (gen_ivtbl_get(clone, &c.ivtbl)) {
|
||
for (i = 0; i < c.ivtbl->numiv; i++)
|
||
c.ivtbl->ivptr[i] = Qundef;
|
||
rb_ary_clear(c.ivtbl);
|
||
}
|
||
else {
|
||
c.ivtbl = gen_ivtbl_resize(0, ivtbl->numiv);
|
||
c.ivtbl = rb_ary_tmp_new(0);
|
||
FL_SET(clone, FL_EXIVAR);
|
||
st_add_direct(generic_iv_tbl, (st_data_t)clone, (st_data_t)c.ivtbl);
|
||
}
|
||
c.iv_index_tbl = iv_index_tbl_make(clone);
|
||
c.obj = clone;
|
||
gen_ivar_each(obj, gen_ivar_copy, (st_data_t)&c);
|
||
/*
|
||
* c.ivtbl may change in gen_ivar_copy due to realloc,
|
||
* no need to free
|
||
*/
|
||
st_insert(generic_iv_tbl, (st_data_t)clone, (st_data_t)c.ivtbl);
|
||
}
|
||
}
|
||
... | ... | |
break;
|
||
default:
|
||
if (FL_TEST(obj, FL_EXIVAR)) {
|
||
struct gen_ivtbl *ivtbl;
|
||
VALUE ivtbl;
|
||
if (gen_ivtbl_get(obj, &ivtbl)) {
|
||
return gen_ivtbl_count(ivtbl);
|
||
-
|