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