|
diff --git a/class.c b/class.c |
|
index df19812..db6b3e5 100644 |
|
--- a/class.c |
|
+++ b/class.c |
|
@@ -31,7 +31,7 @@ |
|
#include "internal.h" |
|
#include <ctype.h> |
|
|
|
-extern st_table *rb_class_tbl; |
|
+extern sa_table rb_class_tbl; |
|
static ID id_attached; |
|
|
|
/** |
|
@@ -50,14 +50,24 @@ static VALUE |
|
class_alloc(VALUE flags, VALUE klass) |
|
{ |
|
rb_classext_t *ext = ALLOC(rb_classext_t); |
|
+ rb_class_cache_t *cache = ALLOC(rb_class_cache_t); |
|
NEWOBJ(obj, struct RClass); |
|
OBJSETUP(obj, klass, flags); |
|
obj->ptr = ext; |
|
- RCLASS_IV_TBL(obj) = 0; |
|
- RCLASS_CONST_TBL(obj) = 0; |
|
- RCLASS_M_TBL(obj) = 0; |
|
- RCLASS_SUPER(obj) = 0; |
|
- RCLASS_IV_INDEX_TBL(obj) = 0; |
|
+ obj->cache = cache; |
|
+ MEMZERO(ext, struct rb_classext_struct, 1); |
|
+ MEMZERO(cache, struct rb_class_cache_struct, 1); |
|
+ return (VALUE)obj; |
|
+} |
|
+ |
|
+static VALUE |
|
+iclass_alloc() |
|
+{ |
|
+ rb_class_cache_t *cache = ALLOC(rb_class_cache_t); |
|
+ NEWOBJ(obj, struct RClass); |
|
+ OBJSETUP(obj, rb_cClass, T_ICLASS); |
|
+ obj->cache = cache; |
|
+ MEMZERO(cache, struct rb_class_cache_struct, 1); |
|
return (VALUE)obj; |
|
} |
|
|
|
@@ -77,7 +87,6 @@ rb_class_boot(VALUE super) |
|
VALUE klass = class_alloc(T_CLASS, rb_cClass); |
|
|
|
RCLASS_SUPER(klass) = super; |
|
- RCLASS_M_TBL(klass) = st_init_numtable(); |
|
|
|
OBJ_INFECT(klass, super); |
|
return (VALUE)klass; |
|
@@ -120,85 +129,59 @@ rb_class_new(VALUE super) |
|
return rb_class_boot(super); |
|
} |
|
|
|
-struct clone_method_data { |
|
- st_table *tbl; |
|
- VALUE klass; |
|
-}; |
|
- |
|
VALUE rb_iseq_clone(VALUE iseqval, VALUE newcbase); |
|
|
|
-static int |
|
-clone_method(ID mid, const rb_method_entry_t *me, struct clone_method_data *data) |
|
+static void |
|
+clone_method(ID mid, const rb_method_entry_t *me, VALUE klass) |
|
{ |
|
VALUE newiseqval; |
|
if (me->def && me->def->type == VM_METHOD_TYPE_ISEQ) { |
|
rb_iseq_t *iseq; |
|
- newiseqval = rb_iseq_clone(me->def->body.iseq->self, data->klass); |
|
+ newiseqval = rb_iseq_clone(me->def->body.iseq->self, klass); |
|
GetISeqPtr(newiseqval, iseq); |
|
- rb_add_method(data->klass, mid, VM_METHOD_TYPE_ISEQ, iseq, me->flag); |
|
+ rb_add_method(klass, mid, VM_METHOD_TYPE_ISEQ, iseq, me->flag); |
|
RB_GC_GUARD(newiseqval); |
|
} |
|
else { |
|
- rb_method_entry_set(data->klass, mid, me, me->flag); |
|
+ rb_method_entry_set(klass, mid, me, me->flag); |
|
} |
|
- return ST_CONTINUE; |
|
} |
|
|
|
-static int |
|
-clone_const(ID key, const rb_const_entry_t *ce, st_table *tbl) |
|
+static void |
|
+clone_const(sa_index_t key, st_data_t ce, sa_table *tbl) |
|
{ |
|
rb_const_entry_t *nce = ALLOC(rb_const_entry_t); |
|
- *nce = *ce; |
|
- st_insert(tbl, key, (st_data_t)nce); |
|
- return ST_CONTINUE; |
|
-} |
|
- |
|
-static int |
|
-clone_const_i(st_data_t key, st_data_t value, st_data_t data) |
|
-{ |
|
- return clone_const((ID)key, (const rb_const_entry_t *)value, (st_table *)data); |
|
+ *nce = *(const rb_const_entry_t*)ce; |
|
+ sa_insert(tbl, (sa_index_t)key, (st_data_t)nce); |
|
} |
|
|
|
/* :nodoc: */ |
|
VALUE |
|
rb_mod_init_copy(VALUE clone, VALUE orig) |
|
{ |
|
+ ID id; |
|
rb_obj_init_copy(clone, orig); |
|
if (!FL_TEST(CLASS_OF(clone), FL_SINGLETON)) { |
|
RBASIC(clone)->klass = rb_singleton_class_clone(orig); |
|
rb_singleton_class_attached(RBASIC(clone)->klass, (VALUE)clone); |
|
} |
|
RCLASS_SUPER(clone) = RCLASS_SUPER(orig); |
|
- if (RCLASS_IV_TBL(orig)) { |
|
- st_data_t id; |
|
|
|
- if (RCLASS_IV_TBL(clone)) { |
|
- st_free_table(RCLASS_IV_TBL(clone)); |
|
- } |
|
- RCLASS_IV_TBL(clone) = st_copy(RCLASS_IV_TBL(orig)); |
|
- CONST_ID(id, "__classpath__"); |
|
- st_delete(RCLASS_IV_TBL(clone), &id, 0); |
|
- CONST_ID(id, "__classid__"); |
|
- st_delete(RCLASS_IV_TBL(clone), &id, 0); |
|
- } |
|
- if (RCLASS_CONST_TBL(orig)) { |
|
- if (RCLASS_CONST_TBL(clone)) { |
|
- rb_free_const_table(RCLASS_CONST_TBL(clone)); |
|
- } |
|
- RCLASS_CONST_TBL(clone) = st_init_numtable(); |
|
- st_foreach(RCLASS_CONST_TBL(orig), clone_const_i, (st_data_t)RCLASS_CONST_TBL(clone)); |
|
- } |
|
- if (RCLASS_M_TBL(orig)) { |
|
- struct clone_method_data data; |
|
+ sa_copy_to(RCLASS_IV_TBL(orig), RCLASS_IV_TBL(clone)); |
|
+ CONST_ID(id, "__classpath__"); |
|
+ sa_delete(RCLASS_IV_TBL(clone), (sa_index_t)id, 0); |
|
+ CONST_ID(id, "__classid__"); |
|
+ sa_delete(RCLASS_IV_TBL(clone), (sa_index_t)id, 0); |
|
|
|
- if (RCLASS_M_TBL(clone)) { |
|
- rb_free_m_table(RCLASS_M_TBL(clone)); |
|
- } |
|
- data.tbl = RCLASS_M_TBL(clone) = st_init_numtable(); |
|
- data.klass = clone; |
|
- st_foreach(RCLASS_M_TBL(orig), clone_method, |
|
- (st_data_t)&data); |
|
- } |
|
+ sa_clear(RCLASS_CONST_TBL(clone)); |
|
+ SA_FOREACH_START(RCLASS_CONST_TBL(orig)); |
|
+ clone_const(entry->key, value, RCLASS_CONST_TBL(clone)); |
|
+ SA_FOREACH_END(); |
|
+ |
|
+ rb_free_m_table(RCLASS_M_TBL(clone)); |
|
+ SA_FOREACH_START(RCLASS_M_TBL(orig)); |
|
+ clone_method(entry->key, (const rb_method_entry_t *)value, clone); |
|
+ SA_FOREACH_END(); |
|
|
|
return clone; |
|
} |
|
@@ -227,7 +210,6 @@ rb_singleton_class_clone(VALUE obj) |
|
if (!FL_TEST(klass, FL_SINGLETON)) |
|
return klass; |
|
else { |
|
- struct clone_method_data data; |
|
/* copy singleton(unnamed) class */ |
|
VALUE clone = class_alloc((RBASIC(klass)->flags & ~(FL_MARK)), 0); |
|
|
|
@@ -239,18 +221,16 @@ rb_singleton_class_clone(VALUE obj) |
|
} |
|
|
|
RCLASS_SUPER(clone) = RCLASS_SUPER(klass); |
|
- if (RCLASS_IV_TBL(klass)) { |
|
- RCLASS_IV_TBL(clone) = st_copy(RCLASS_IV_TBL(klass)); |
|
- } |
|
- if (RCLASS_CONST_TBL(klass)) { |
|
- RCLASS_CONST_TBL(clone) = st_init_numtable(); |
|
- st_foreach(RCLASS_CONST_TBL(klass), clone_const_i, (st_data_t)RCLASS_CONST_TBL(clone)); |
|
- } |
|
- RCLASS_M_TBL(clone) = st_init_numtable(); |
|
- data.tbl = RCLASS_M_TBL(clone); |
|
- data.klass = (VALUE)clone; |
|
- st_foreach(RCLASS_M_TBL(klass), clone_method, |
|
- (st_data_t)&data); |
|
+ sa_copy_to(RCLASS_IV_TBL(klass), RCLASS_IV_TBL(clone)); |
|
+ |
|
+ SA_FOREACH_START(RCLASS_CONST_TBL(klass)); |
|
+ clone_const(entry->key, value, RCLASS_CONST_TBL(clone)); |
|
+ SA_FOREACH_END(); |
|
+ |
|
+ SA_FOREACH_START(RCLASS_M_TBL(klass)); |
|
+ clone_method(entry->key, (const rb_method_entry_t*)value, clone); |
|
+ SA_FOREACH_END(); |
|
+ |
|
rb_singleton_class_attached(RBASIC(clone)->klass, (VALUE)clone); |
|
FL_SET(clone, FL_SINGLETON); |
|
return (VALUE)clone; |
|
@@ -265,10 +245,7 @@ void |
|
rb_singleton_class_attached(VALUE klass, VALUE obj) |
|
{ |
|
if (FL_TEST(klass, FL_SINGLETON)) { |
|
- if (!RCLASS_IV_TBL(klass)) { |
|
- RCLASS_IV_TBL(klass) = st_init_numtable(); |
|
- } |
|
- st_insert(RCLASS_IV_TBL(klass), id_attached, obj); |
|
+ sa_insert(RCLASS_IV_TBL(klass), (sa_index_t)id_attached, obj); |
|
} |
|
} |
|
|
|
@@ -355,12 +332,12 @@ make_singleton_class(VALUE obj) |
|
static VALUE |
|
boot_defclass(const char *name, VALUE super) |
|
{ |
|
- extern st_table *rb_class_tbl; |
|
+ extern sa_table rb_class_tbl; |
|
VALUE obj = rb_class_boot(super); |
|
ID id = rb_intern(name); |
|
|
|
rb_name_class(obj, id); |
|
- st_add_direct(rb_class_tbl, id, obj); |
|
+ sa_insert(&rb_class_tbl, (sa_index_t)id, obj); |
|
rb_const_set((rb_cObject ? rb_cObject : obj), id, obj); |
|
return obj; |
|
} |
|
@@ -484,7 +461,7 @@ rb_define_class(const char *name, VALUE super) |
|
rb_warn("no super class for `%s', Object assumed", name); |
|
} |
|
klass = rb_define_class_id(id, super); |
|
- st_add_direct(rb_class_tbl, id, klass); |
|
+ sa_insert(&rb_class_tbl, (sa_index_t)id, klass); |
|
rb_name_class(klass, id); |
|
rb_const_set(rb_cObject, id, klass); |
|
rb_class_inherited(super, klass); |
|
@@ -565,8 +542,6 @@ rb_module_new(void) |
|
{ |
|
VALUE mdl = class_alloc(T_MODULE, rb_cModule); |
|
|
|
- RCLASS_M_TBL(mdl) = st_init_numtable(); |
|
- |
|
return (VALUE)mdl; |
|
} |
|
|
|
@@ -595,7 +570,7 @@ rb_define_module(const char *name) |
|
rb_raise(rb_eTypeError, "%s is not a module", rb_obj_classname(module)); |
|
} |
|
module = rb_define_module_id(id); |
|
- st_add_direct(rb_class_tbl, id, module); |
|
+ sa_insert(&rb_class_tbl, (sa_index_t)id, module); |
|
rb_const_set(rb_cObject, id, module); |
|
|
|
return module; |
|
@@ -630,27 +605,15 @@ rb_define_module_id_under(VALUE outer, ID id) |
|
static VALUE |
|
include_class_new(VALUE module, VALUE super) |
|
{ |
|
- VALUE klass = class_alloc(T_ICLASS, rb_cClass); |
|
+ VALUE klass; |
|
|
|
if (BUILTIN_TYPE(module) == T_ICLASS) { |
|
module = RBASIC(module)->klass; |
|
} |
|
- if (!RCLASS_IV_TBL(module)) { |
|
- RCLASS_IV_TBL(module) = st_init_numtable(); |
|
- } |
|
- if (!RCLASS_CONST_TBL(module)) { |
|
- RCLASS_CONST_TBL(module) = st_init_numtable(); |
|
- } |
|
- RCLASS_IV_TBL(klass) = RCLASS_IV_TBL(module); |
|
- RCLASS_CONST_TBL(klass) = RCLASS_CONST_TBL(module); |
|
- RCLASS_M_TBL(klass) = RCLASS_M_TBL(module); |
|
+ klass = iclass_alloc(); |
|
+ RBASIC(klass)->klass = module; |
|
+ RCLASS_EXT(klass) = RCLASS_EXT(module); |
|
RCLASS_SUPER(klass) = super; |
|
- if (TYPE(module) == T_ICLASS) { |
|
- RBASIC(klass)->klass = RBASIC(module)->klass; |
|
- } |
|
- else { |
|
- RBASIC(klass)->klass = module; |
|
- } |
|
OBJ_INFECT(klass, module); |
|
OBJ_INFECT(klass, super); |
|
|
|
@@ -677,13 +640,13 @@ rb_include_module(VALUE klass, VALUE module) |
|
while (module) { |
|
int superclass_seen = FALSE; |
|
|
|
- if (RCLASS_M_TBL(klass) == RCLASS_M_TBL(module)) |
|
+ if (RCLASS_EXT(klass) == RCLASS_EXT(module)) |
|
rb_raise(rb_eArgError, "cyclic include detected"); |
|
/* ignore if the module included already in superclasses */ |
|
for (p = RCLASS_SUPER(klass); p; p = RCLASS_SUPER(p)) { |
|
switch (BUILTIN_TYPE(p)) { |
|
case T_ICLASS: |
|
- if (RCLASS_M_TBL(p) == RCLASS_M_TBL(module)) { |
|
+ if (RCLASS_EXT(p) == RCLASS_EXT(module)) { |
|
if (!superclass_seen) { |
|
c = p; /* move insertion point */ |
|
} |
|
@@ -696,7 +659,7 @@ rb_include_module(VALUE klass, VALUE module) |
|
} |
|
} |
|
c = RCLASS_SUPER(c) = include_class_new(module, RCLASS_SUPER(c)); |
|
- if (RMODULE_M_TBL(module) && RMODULE_M_TBL(module)->num_entries) |
|
+ if (RMODULE_M_TBL(module)->num_entries) |
|
changed = 1; |
|
skip: |
|
module = RCLASS_SUPER(module); |
|
@@ -827,58 +790,58 @@ ins_methods_push(ID name, long type, VALUE ary, long visi) |
|
} |
|
|
|
static int |
|
-ins_methods_i(st_data_t name, st_data_t type, st_data_t ary) |
|
+ins_methods_i(sa_index_t name, st_data_t type, st_data_t ary) |
|
{ |
|
return ins_methods_push((ID)name, (long)type, (VALUE)ary, -1); /* everything but private */ |
|
} |
|
|
|
static int |
|
-ins_methods_prot_i(st_data_t name, st_data_t type, st_data_t ary) |
|
+ins_methods_prot_i(sa_index_t name, st_data_t type, st_data_t ary) |
|
{ |
|
return ins_methods_push((ID)name, (long)type, (VALUE)ary, NOEX_PROTECTED); |
|
} |
|
|
|
static int |
|
-ins_methods_priv_i(st_data_t name, st_data_t type, st_data_t ary) |
|
+ins_methods_priv_i(sa_index_t name, st_data_t type, st_data_t ary) |
|
{ |
|
return ins_methods_push((ID)name, (long)type, (VALUE)ary, NOEX_PRIVATE); |
|
} |
|
|
|
static int |
|
-ins_methods_pub_i(st_data_t name, st_data_t type, st_data_t ary) |
|
+ins_methods_pub_i(sa_index_t name, st_data_t type, st_data_t ary) |
|
{ |
|
return ins_methods_push((ID)name, (long)type, (VALUE)ary, NOEX_PUBLIC); |
|
} |
|
|
|
static int |
|
-method_entry_i(st_data_t key, st_data_t value, st_data_t data) |
|
+method_entry_i(st_index_t key, st_data_t value, st_data_t data) |
|
{ |
|
const rb_method_entry_t *me = (const rb_method_entry_t *)value; |
|
- st_table *list = (st_table *)data; |
|
+ sa_table *list = (sa_table *)data; |
|
long type; |
|
|
|
if ((ID)key == ID_ALLOCATOR) { |
|
return ST_CONTINUE; |
|
} |
|
|
|
- if (!st_lookup(list, key, 0)) { |
|
+ if (!sa_lookup(list, key, 0)) { |
|
if (UNDEFINED_METHOD_ENTRY_P(me)) { |
|
type = -1; /* none */ |
|
} |
|
else { |
|
type = VISI(me->flag); |
|
} |
|
- st_add_direct(list, key, type); |
|
+ sa_insert(list, key, type); |
|
} |
|
return ST_CONTINUE; |
|
} |
|
|
|
static VALUE |
|
-class_instance_method_list(int argc, VALUE *argv, VALUE mod, int obj, int (*func) (st_data_t, st_data_t, st_data_t)) |
|
+class_instance_method_list(int argc, VALUE *argv, VALUE mod, int obj, int (*func) (sa_index_t, st_data_t, st_data_t)) |
|
{ |
|
VALUE ary; |
|
int recur; |
|
- st_table *list; |
|
+ sa_table list = SA_EMPTY_TABLE; |
|
|
|
if (argc == 0) { |
|
recur = TRUE; |
|
@@ -889,16 +852,14 @@ class_instance_method_list(int argc, VALUE *argv, VALUE mod, int obj, int (*func |
|
recur = RTEST(r); |
|
} |
|
|
|
- list = st_init_numtable(); |
|
for (; mod; mod = RCLASS_SUPER(mod)) { |
|
- st_foreach(RCLASS_M_TBL(mod), method_entry_i, (st_data_t)list); |
|
+ sa_foreach(RCLASS_M_TBL(mod), method_entry_i, (st_data_t)&list); |
|
if (BUILTIN_TYPE(mod) == T_ICLASS) continue; |
|
if (obj && FL_TEST(mod, FL_SINGLETON)) continue; |
|
if (!recur) break; |
|
} |
|
ary = rb_ary_new(); |
|
- st_foreach(list, func, ary); |
|
- st_free_table(list); |
|
+ sa_foreach(&list, func, ary); |
|
|
|
return ary; |
|
} |
|
@@ -1112,7 +1073,7 @@ VALUE |
|
rb_obj_singleton_methods(int argc, VALUE *argv, VALUE obj) |
|
{ |
|
VALUE recur, ary, klass; |
|
- st_table *list; |
|
+ sa_table list = SA_EMPTY_TABLE; |
|
|
|
if (argc == 0) { |
|
recur = Qtrue; |
|
@@ -1121,20 +1082,18 @@ rb_obj_singleton_methods(int argc, VALUE *argv, VALUE obj) |
|
rb_scan_args(argc, argv, "01", &recur); |
|
} |
|
klass = CLASS_OF(obj); |
|
- list = st_init_numtable(); |
|
if (klass && FL_TEST(klass, FL_SINGLETON)) { |
|
- st_foreach(RCLASS_M_TBL(klass), method_entry_i, (st_data_t)list); |
|
+ sa_foreach(RCLASS_M_TBL(klass), method_entry_i, (st_data_t)&list); |
|
klass = RCLASS_SUPER(klass); |
|
} |
|
if (RTEST(recur)) { |
|
while (klass && (FL_TEST(klass, FL_SINGLETON) || TYPE(klass) == T_ICLASS)) { |
|
- st_foreach(RCLASS_M_TBL(klass), method_entry_i, (st_data_t)list); |
|
+ sa_foreach(RCLASS_M_TBL(klass), method_entry_i, (st_data_t)&list); |
|
klass = RCLASS_SUPER(klass); |
|
} |
|
} |
|
ary = rb_ary_new(); |
|
- st_foreach(list, ins_methods_i, ary); |
|
- st_free_table(list); |
|
+ sa_foreach(&list, ins_methods_i, ary); |
|
|
|
return ary; |
|
} |
|
diff --git a/common.mk b/common.mk |
|
index eb89a2b..b65dab6 100644 |
|
--- a/common.mk |
|
+++ b/common.mk |
|
@@ -78,6 +78,7 @@ COMMONOBJS = array.$(OBJEXT) \ |
|
safe.$(OBJEXT) \ |
|
signal.$(OBJEXT) \ |
|
sprintf.$(OBJEXT) \ |
|
+ sp_ar.$(OBJEXT) \ |
|
st.$(OBJEXT) \ |
|
strftime.$(OBJEXT) \ |
|
string.$(OBJEXT) \ |
|
@@ -630,7 +631,8 @@ file.$(OBJEXT): {$(VPATH)}file.c $(RUBY_H_INCLUDES) {$(VPATH)}io.h \ |
|
gc.$(OBJEXT): {$(VPATH)}gc.c $(RUBY_H_INCLUDES) {$(VPATH)}re.h \ |
|
{$(VPATH)}regex.h $(ENCODING_H_INCLUDES) $(VM_CORE_H_INCLUDES) \ |
|
{$(VPATH)}gc.h {$(VPATH)}io.h {$(VPATH)}eval_intern.h {$(VPATH)}util.h \ |
|
- {$(VPATH)}debug.h {$(VPATH)}internal.h {$(VPATH)}constant.h |
|
+ {$(VPATH)}debug.h {$(VPATH)}internal.h {$(VPATH)}constant.h \ |
|
+ {$(VPATH)}pool_alloc.inc.h {$(VPATH)}pool_alloc.h |
|
hash.$(OBJEXT): {$(VPATH)}hash.c $(RUBY_H_INCLUDES) {$(VPATH)}util.h \ |
|
$(ENCODING_H_INCLUDES) |
|
inits.$(OBJEXT): {$(VPATH)}inits.c $(RUBY_H_INCLUDES) \ |
|
@@ -693,7 +695,8 @@ signal.$(OBJEXT): {$(VPATH)}signal.c $(RUBY_H_INCLUDES) \ |
|
$(VM_CORE_H_INCLUDES) {$(VPATH)}debug.h |
|
sprintf.$(OBJEXT): {$(VPATH)}sprintf.c $(RUBY_H_INCLUDES) {$(VPATH)}re.h \ |
|
{$(VPATH)}regex.h {$(VPATH)}vsnprintf.c $(ENCODING_H_INCLUDES) |
|
-st.$(OBJEXT): {$(VPATH)}st.c $(RUBY_H_INCLUDES) |
|
+sp_ar.$(OBJEXT): {$(VPATH)}sp_ar.c $(RUBY_H_INCLUDES) |
|
+st.$(OBJEXT): {$(VPATH)}st.c $(RUBY_H_INCLUDES) {$(VPATH)}pool_alloc.h |
|
strftime.$(OBJEXT): {$(VPATH)}strftime.c $(RUBY_H_INCLUDES) \ |
|
{$(VPATH)}timev.h |
|
string.$(OBJEXT): {$(VPATH)}string.c $(RUBY_H_INCLUDES) {$(VPATH)}re.h \ |
|
diff --git a/configure.in b/configure.in |
|
index be818d4..88fe110 100644 |
|
--- a/configure.in |
|
+++ b/configure.in |
|
@@ -1316,6 +1316,29 @@ main() { |
|
CFLAGS="$save_CFLAGS"]) |
|
AC_DEFINE_UNQUOTED(GC_MARK_STACKFRAME_WORD, $rb_cv_gc_mark_stackframe_word) |
|
|
|
+AS_CASE(["$target_os"], |
|
+[openbsd*], [ |
|
+ AC_CACHE_CHECK(for heap align log on openbsd, rb_cv_page_size_log, |
|
+ [rb_cv_page_size_log=no |
|
+ for page_log in 12 13; do |
|
+ AC_TRY_RUN([ |
|
+#include <math.h> |
|
+#include <unistd.h> |
|
+ |
|
+int |
|
+main() { |
|
+ if ((int)log2((double)sysconf(_SC_PAGESIZE)) != $page_log) return 1; |
|
+ return 0; |
|
+} |
|
+ ], |
|
+ rb_cv_page_size_log="$page_log"; break) |
|
+ done]) |
|
+ if test $rb_cv_page_size_log != no; then |
|
+ AC_DEFINE_UNQUOTED(HEAP_ALIGN_LOG, $rb_cv_page_size_log) |
|
+ else |
|
+ AC_DEFINE_UNQUOTED(HEAP_ALIGN_LOG, 12) |
|
+ fi |
|
+]) |
|
|
|
dnl Checks for library functions. |
|
AC_TYPE_GETGROUPS |
|
@@ -1416,7 +1439,8 @@ AC_CHECK_FUNCS(fmod killpg wait4 waitpid fork spawnv syscall __syscall chroot ge |
|
setsid telldir seekdir fchmod cosh sinh tanh log2 round\ |
|
setuid setgid daemon select_large_fdset setenv unsetenv\ |
|
mktime timegm gmtime_r clock_gettime gettimeofday poll ppoll\ |
|
- pread sendfile shutdown sigaltstack dl_iterate_phdr) |
|
+ pread sendfile shutdown sigaltstack dl_iterate_phdr\ |
|
+ dup3 pipe2 posix_memalign memalign) |
|
|
|
AC_CACHE_CHECK(for unsetenv returns a value, rb_cv_unsetenv_return_value, |
|
[AC_TRY_COMPILE([ |
|
diff --git a/constant.h b/constant.h |
|
index 8232910..9106847 100644 |
|
--- a/constant.h |
|
+++ b/constant.h |
|
@@ -23,7 +23,7 @@ typedef struct rb_const_entry_struct { |
|
|
|
VALUE rb_mod_private_constant(int argc, VALUE *argv, VALUE obj); |
|
VALUE rb_mod_public_constant(int argc, VALUE *argv, VALUE obj); |
|
-void rb_free_const_table(st_table *tbl); |
|
+void rb_free_const_table(sa_table *tbl); |
|
VALUE rb_public_const_get(VALUE klass, ID id); |
|
VALUE rb_public_const_get_at(VALUE klass, ID id); |
|
VALUE rb_public_const_get_from(VALUE klass, ID id); |
|
diff --git a/ext/-test-/st/numhash/numhash.c b/ext/-test-/st/numhash/numhash.c |
|
index e186cd4..53d9e1b 100644 |
|
--- a/ext/-test-/st/numhash/numhash.c |
|
+++ b/ext/-test-/st/numhash/numhash.c |
|
@@ -54,7 +54,7 @@ numhash_i(st_data_t key, st_data_t value, st_data_t arg, int error) |
|
static VALUE |
|
numhash_each(VALUE self) |
|
{ |
|
- return st_foreach((st_table *)DATA_PTR(self), numhash_i, self) ? Qtrue : Qfalse; |
|
+ return st_foreach_check((st_table *)DATA_PTR(self), numhash_i, self, 0) ? Qtrue : Qfalse; |
|
} |
|
|
|
void |
|
diff --git a/ext/objspace/objspace.c b/ext/objspace/objspace.c |
|
index 66e33a3..4b31f92 100644 |
|
--- a/ext/objspace/objspace.c |
|
+++ b/ext/objspace/objspace.c |
|
@@ -60,20 +60,10 @@ memsize_of(VALUE obj) |
|
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); |
|
+ size += sa_memsize(RCLASS_M_TBL(obj)); |
|
+ size += sa_memsize(RCLASS_IV_TBL(obj)); |
|
+ size += sa_memsize(RCLASS_IV_INDEX_TBL(obj)); |
|
+ size += sa_memsize(RCLASS_CONST_TBL(obj)); |
|
break; |
|
case T_STRING: |
|
size += rb_str_memsize(obj); |
|
diff --git a/gc.c b/gc.c |
|
index e65d0ec..ee183be 100644 |
|
--- a/gc.c |
|
+++ b/gc.c |
|
@@ -20,10 +20,12 @@ |
|
#include "vm_core.h" |
|
#include "internal.h" |
|
#include "gc.h" |
|
+#include "pool_alloc.h" |
|
#include "constant.h" |
|
#include <stdio.h> |
|
#include <setjmp.h> |
|
#include <sys/types.h> |
|
+#include <assert.h> |
|
|
|
#ifdef HAVE_SYS_TIME_H |
|
#include <sys/time.h> |
|
@@ -35,7 +37,12 @@ |
|
|
|
#if defined _WIN32 || defined __CYGWIN__ |
|
#include <windows.h> |
|
+#elif defined(HAVE_POSIX_MEMALIGN) |
|
+#elif defined(HAVE_MEMALIGN) |
|
+#include <malloc.h> |
|
#endif |
|
+static void aligned_free(void *); |
|
+static void *aligned_malloc(size_t alignment, size_t size); |
|
|
|
#ifdef HAVE_VALGRIND_MEMCHECK_H |
|
# include <valgrind/memcheck.h> |
|
@@ -321,6 +328,24 @@ struct gc_list { |
|
|
|
#define CALC_EXACT_MALLOC_SIZE 0 |
|
|
|
+#ifdef POOL_ALLOC_API |
|
+/* POOL ALLOC API */ |
|
+#define POOL_ALLOC_PART 1 |
|
+#include "pool_alloc.inc.h" |
|
+#undef POOL_ALLOC_PART |
|
+ |
|
+typedef struct pool_layout_t pool_layout_t; |
|
+struct pool_layout_t { |
|
+ pool_header |
|
+ p6, /* st_table && st_table_entry */ |
|
+ p11; /* st_table.bins init size */ |
|
+} pool_layout = { |
|
+ INIT_POOL(void*[6]), |
|
+ INIT_POOL(void*[11]) |
|
+}; |
|
+static void pool_finalize_header(pool_header *header); |
|
+#endif |
|
+ |
|
typedef struct rb_objspace { |
|
struct { |
|
size_t limit; |
|
@@ -330,6 +355,9 @@ typedef struct rb_objspace { |
|
size_t allocations; |
|
#endif |
|
} malloc_params; |
|
+#ifdef POOL_ALLOC_API |
|
+ pool_layout_t *pool_headers; |
|
+#endif |
|
struct { |
|
size_t increment; |
|
struct heaps_slot *ptr; |
|
@@ -377,7 +405,11 @@ typedef struct rb_objspace { |
|
#define ruby_initial_gc_stress initial_params.gc_stress |
|
int *ruby_initial_gc_stress_ptr = &ruby_initial_gc_stress; |
|
#else |
|
+# ifdef POOL_ALLOC_API |
|
+static rb_objspace_t rb_objspace = {{GC_MALLOC_LIMIT}, &pool_layout, {HEAP_MIN_SLOTS}}; |
|
+# else |
|
static rb_objspace_t rb_objspace = {{GC_MALLOC_LIMIT}, {HEAP_MIN_SLOTS}}; |
|
+# endif |
|
int *ruby_initial_gc_stress_ptr = &rb_objspace.gc_stress; |
|
#endif |
|
#define malloc_limit objspace->malloc_params.limit |
|
@@ -413,6 +445,10 @@ rb_objspace_alloc(void) |
|
memset(objspace, 0, sizeof(*objspace)); |
|
malloc_limit = initial_malloc_limit; |
|
ruby_gc_stress = ruby_initial_gc_stress; |
|
+#ifdef POOL_ALLOC_API |
|
+ objspace->pool_headers = (pool_layout_t*) malloc(sizeof(pool_layout)); |
|
+ memcpy(objspace->pool_headers, &pool_layout, sizeof(pool_layout)); |
|
+#endif |
|
|
|
return objspace; |
|
} |
|
@@ -491,6 +527,13 @@ rb_objspace_free(rb_objspace_t *objspace) |
|
heaps_used = 0; |
|
heaps = 0; |
|
} |
|
+#ifdef POOL_ALLOC_API |
|
+ if (objspace->pool_headers) { |
|
+ pool_finalize_header(&objspace->pool_headers->p6); |
|
+ pool_finalize_header(&objspace->pool_headers->p11); |
|
+ free(objspace->pool_headers); |
|
+ } |
|
+#endif |
|
free(objspace); |
|
} |
|
#endif |
|
@@ -513,7 +556,7 @@ rb_objspace_free(rb_objspace_t *objspace) |
|
|
|
#define HEAP_OBJ_LIMIT (unsigned int)(HEAP_SIZE / sizeof(struct RVALUE)) |
|
|
|
-extern st_table *rb_class_tbl; |
|
+extern sa_table rb_class_tbl; |
|
|
|
int ruby_disable_gc_stress = 0; |
|
|
|
@@ -894,6 +937,27 @@ ruby_xfree(void *x) |
|
vm_xfree(&rb_objspace, x); |
|
} |
|
|
|
+#ifdef POOL_ALLOC_API |
|
+/* POOL ALLOC API */ |
|
+#define POOL_ALLOC_PART 2 |
|
+#include "pool_alloc.inc.h" |
|
+#undef POOL_ALLOC_PART |
|
+ |
|
+void |
|
+ruby_xpool_free(void *ptr) |
|
+{ |
|
+ pool_free_entry((void**)ptr); |
|
+} |
|
+ |
|
+#define CONCRET_POOL_MALLOC(pnts) \ |
|
+void * ruby_xpool_malloc_##pnts##p () { \ |
|
+ return pool_alloc_entry(&rb_objspace.pool_headers->p##pnts ); \ |
|
+} |
|
+CONCRET_POOL_MALLOC(6) |
|
+CONCRET_POOL_MALLOC(11) |
|
+#undef CONCRET_POOL_MALLOC |
|
+ |
|
+#endif |
|
|
|
/* |
|
* call-seq: |
|
@@ -1008,6 +1072,55 @@ allocate_sorted_heaps(rb_objspace_t *objspace, size_t next_heaps_length) |
|
heaps_length = next_heaps_length; |
|
} |
|
|
|
+static void * |
|
+aligned_malloc(size_t alignment, size_t size) |
|
+{ |
|
+ void *res; |
|
+ |
|
+#if defined __MINGW32__ |
|
+ res = __mingw_aligned_malloc(size, alignment); |
|
+#elif defined _WIN32 && !defined __CYGWIN__ |
|
+ res = _aligned_malloc(size, alignment); |
|
+#elif defined(HAVE_POSIX_MEMALIGN) |
|
+ if (posix_memalign(&res, alignment, size) == 0) { |
|
+ return res; |
|
+ } |
|
+ else { |
|
+ return NULL; |
|
+ } |
|
+#elif defined(HAVE_MEMALIGN) |
|
+ res = memalign(alignment, size); |
|
+#else |
|
+ char* aligned; |
|
+ res = malloc(alignment + size + sizeof(void*)); |
|
+ aligned = (char*)res + alignment + sizeof(void*); |
|
+ aligned -= ((VALUE)aligned & (alignment - 1)); |
|
+ ((void**)aligned)[-1] = res; |
|
+ res = (void*)aligned; |
|
+#endif |
|
+ |
|
+#if defined(_DEBUG) || defined(GC_DEBUG) |
|
+ /* alignment must be a power of 2 */ |
|
+ assert((alignment - 1) & alignment == 0); |
|
+ assert(alignment % sizeof(void*) == 0); |
|
+#endif |
|
+ return res; |
|
+} |
|
+ |
|
+static void |
|
+aligned_free(void *ptr) |
|
+{ |
|
+#if defined __MINGW32__ |
|
+ __mingw_aligned_free(ptr); |
|
+#elif defined _WIN32 && !defined __CYGWIN__ |
|
+ _aligned_free(ptr); |
|
+#elif defined(HAVE_MEMALIGN) || defined(HAVE_POSIX_MEMALIGN) |
|
+ free(ptr); |
|
+#else |
|
+ free(((void**)ptr)[-1]); |
|
+#endif |
|
+} |
|
+ |
|
static void |
|
assign_heap_slot(rb_objspace_t *objspace) |
|
{ |
|
@@ -1466,6 +1579,15 @@ mark_tbl(rb_objspace_t *objspace, st_table *tbl, int lev) |
|
st_foreach(tbl, mark_entry, (st_data_t)&arg); |
|
} |
|
|
|
+static void |
|
+mark_sa_tbl(rb_objspace_t *objspace, sa_table *tbl, int lev) |
|
+{ |
|
+ if (!tbl) return; |
|
+ SA_FOREACH_START(tbl); |
|
+ gc_mark(objspace, (VALUE)value, lev); |
|
+ SA_FOREACH_END(); |
|
+} |
|
+ |
|
static int |
|
mark_key(VALUE key, VALUE value, st_data_t data) |
|
{ |
|
@@ -1544,74 +1666,52 @@ rb_mark_method_entry(const rb_method_entry_t *me) |
|
mark_method_entry(&rb_objspace, me, 0); |
|
} |
|
|
|
-static int |
|
-mark_method_entry_i(ID key, const rb_method_entry_t *me, st_data_t data) |
|
-{ |
|
- struct mark_tbl_arg *arg = (void*)data; |
|
- mark_method_entry(arg->objspace, me, arg->lev); |
|
- return ST_CONTINUE; |
|
-} |
|
- |
|
static void |
|
-mark_m_tbl(rb_objspace_t *objspace, st_table *tbl, int lev) |
|
-{ |
|
- struct mark_tbl_arg arg; |
|
- if (!tbl) return; |
|
- arg.objspace = objspace; |
|
- arg.lev = lev; |
|
- st_foreach(tbl, mark_method_entry_i, (st_data_t)&arg); |
|
-} |
|
- |
|
-static int |
|
-free_method_entry_i(ID key, rb_method_entry_t *me, st_data_t data) |
|
+mark_m_tbl(rb_objspace_t *objspace, sa_table *tbl, int lev) |
|
{ |
|
- rb_free_method_entry(me); |
|
- return ST_CONTINUE; |
|
+ SA_FOREACH_START(tbl); |
|
+ mark_method_entry(objspace, (const rb_method_entry_t*)value, lev); |
|
+ SA_FOREACH_END(); |
|
} |
|
|
|
void |
|
-rb_free_m_table(st_table *tbl) |
|
+rb_free_m_table(sa_table *tbl) |
|
{ |
|
- st_foreach(tbl, free_method_entry_i, 0); |
|
- st_free_table(tbl); |
|
-} |
|
- |
|
-static int |
|
-mark_const_entry_i(ID key, const rb_const_entry_t *ce, st_data_t data) |
|
-{ |
|
- struct mark_tbl_arg *arg = (void*)data; |
|
- gc_mark(arg->objspace, ce->value, arg->lev); |
|
- return ST_CONTINUE; |
|
+ SA_FOREACH_START(tbl); |
|
+ if (!((rb_method_entry_t*)value)->mark) { |
|
+ rb_free_method_entry((rb_method_entry_t*)value); |
|
+ } |
|
+ SA_FOREACH_END(); |
|
+ sa_clear(tbl); |
|
} |
|
|
|
static void |
|
-mark_const_tbl(rb_objspace_t *objspace, st_table *tbl, int lev) |
|
+mark_const_tbl(rb_objspace_t *objspace, sa_table *tbl, int lev) |
|
{ |
|
- struct mark_tbl_arg arg; |
|
- if (!tbl) return; |
|
- arg.objspace = objspace; |
|
- arg.lev = lev; |
|
- st_foreach(tbl, mark_const_entry_i, (st_data_t)&arg); |
|
+ SA_FOREACH_START(tbl); |
|
+ gc_mark(objspace, ((const rb_const_entry_t*)value)->value, lev); |
|
+ SA_FOREACH_END(); |
|
} |
|
|
|
-static int |
|
-free_const_entry_i(ID key, rb_const_entry_t *ce, st_data_t data) |
|
+void |
|
+rb_free_const_table(sa_table *tbl) |
|
{ |
|
- xfree(ce); |
|
- return ST_CONTINUE; |
|
+ SA_FOREACH_START(tbl); |
|
+ xfree((rb_const_entry_t*)value); |
|
+ SA_FOREACH_END(); |
|
+ sa_clear(tbl); |
|
} |
|
|
|
void |
|
-rb_free_const_table(st_table *tbl) |
|
+rb_mark_tbl(st_table *tbl) |
|
{ |
|
- st_foreach(tbl, free_const_entry_i, 0); |
|
- st_free_table(tbl); |
|
+ mark_tbl(&rb_objspace, tbl, 0); |
|
} |
|
|
|
void |
|
-rb_mark_tbl(st_table *tbl) |
|
+rb_mark_sa_tbl(sa_table *tbl) |
|
{ |
|
- mark_tbl(&rb_objspace, tbl, 0); |
|
+ mark_sa_tbl(&rb_objspace, tbl, 0); |
|
} |
|
|
|
void |
|
@@ -1819,7 +1919,7 @@ gc_mark_children(rb_objspace_t *objspace, VALUE ptr, int lev) |
|
case T_CLASS: |
|
case T_MODULE: |
|
mark_m_tbl(objspace, RCLASS_M_TBL(obj), lev); |
|
- mark_tbl(objspace, RCLASS_IV_TBL(obj), lev); |
|
+ mark_sa_tbl(objspace, RCLASS_IV_TBL(obj), lev); |
|
mark_const_tbl(objspace, RCLASS_CONST_TBL(obj), lev); |
|
ptr = RCLASS_SUPER(obj); |
|
goto again; |
|
@@ -2286,15 +2386,11 @@ obj_free(rb_objspace_t *objspace, VALUE obj) |
|
case T_CLASS: |
|
rb_clear_cache_by_class((VALUE)obj); |
|
rb_free_m_table(RCLASS_M_TBL(obj)); |
|
- if (RCLASS_IV_TBL(obj)) { |
|
- st_free_table(RCLASS_IV_TBL(obj)); |
|
- } |
|
- if (RCLASS_CONST_TBL(obj)) { |
|
- rb_free_const_table(RCLASS_CONST_TBL(obj)); |
|
- } |
|
- if (RCLASS_IV_INDEX_TBL(obj)) { |
|
- st_free_table(RCLASS_IV_INDEX_TBL(obj)); |
|
- } |
|
+ sa_clear(RCLASS_IV_TBL(obj)); |
|
+ rb_free_const_table(RCLASS_CONST_TBL(obj)); |
|
+ sa_clear(RCLASS_IV_INDEX_TBL(obj)); |
|
+ sa_clear(&RCLASS(obj)->cache->m_cache_tbl); |
|
+ xfree(RCLASS(obj)->cache); |
|
xfree(RANY(obj)->as.klass.ptr); |
|
break; |
|
case T_STRING: |
|
@@ -2346,8 +2442,9 @@ obj_free(rb_objspace_t *objspace, VALUE obj) |
|
case T_COMPLEX: |
|
break; |
|
case T_ICLASS: |
|
+ sa_clear(&RCLASS(obj)->cache->m_cache_tbl); |
|
+ xfree(RCLASS(obj)->cache); |
|
/* iClass shares table with the module */ |
|
- xfree(RANY(obj)->as.klass.ptr); |
|
break; |
|
|
|
case T_FLOAT: |
|
@@ -2458,7 +2555,7 @@ gc_marks(rb_objspace_t *objspace) |
|
rb_mark_end_proc(); |
|
rb_gc_mark_global_tbl(); |
|
|
|
- mark_tbl(objspace, rb_class_tbl, 0); |
|
+ mark_sa_tbl(objspace, &rb_class_tbl, 0); |
|
|
|
/* mark generic instance variables for special constants */ |
|
rb_mark_generic_ivar_tbl(); |
|
diff --git a/hash.c b/hash.c |
|
index fbd8237..32917c3 100644 |
|
--- a/hash.c |
|
+++ b/hash.c |
|
@@ -44,7 +44,7 @@ rb_any_cmp(VALUE a, VALUE b) |
|
if (FIXNUM_P(a) && FIXNUM_P(b)) { |
|
return a != b; |
|
} |
|
- if (TYPE(a) == T_STRING && RBASIC(a)->klass == rb_cString && |
|
+ if (RB_TYPE_P(a, T_STRING) && RBASIC(a)->klass == rb_cString && |
|
TYPE(b) == T_STRING && RBASIC(b)->klass == rb_cString) { |
|
return rb_str_hash_cmp(a, b); |
|
} |
|
@@ -80,20 +80,14 @@ rb_any_hash(VALUE a) |
|
VALUE hval; |
|
st_index_t hnum; |
|
|
|
- switch (TYPE(a)) { |
|
- case T_FIXNUM: |
|
- case T_SYMBOL: |
|
- case T_NIL: |
|
- case T_FALSE: |
|
- case T_TRUE: |
|
- hnum = rb_hash_end(rb_hash_start((unsigned int)a)); |
|
- break; |
|
- |
|
- case T_STRING: |
|
+ if (SPECIAL_CONST_P(a)) { |
|
+ if (a == Qundef) return 0; |
|
+ hnum = rb_hash_end(rb_hash_start((st_index_t)a)); |
|
+ } |
|
+ else if (BUILTIN_TYPE(a) == T_STRING) { |
|
hnum = rb_str_hash(a); |
|
- break; |
|
- |
|
- default: |
|
+ } |
|
+ else { |
|
hval = rb_hash(a); |
|
hnum = FIX2LONG(hval); |
|
} |
|
@@ -106,10 +100,8 @@ static const struct st_hash_type objhash = { |
|
rb_any_hash, |
|
}; |
|
|
|
-static const struct st_hash_type identhash = { |
|
- st_numcmp, |
|
- st_numhash, |
|
-}; |
|
+extern const struct st_hash_type st_hashtype_num; |
|
+#define identhash st_hashtype_num |
|
|
|
typedef int st_foreach_func(st_data_t, st_data_t, st_data_t); |
|
|
|
@@ -124,7 +116,6 @@ foreach_safe_i(st_data_t key, st_data_t value, struct foreach_safe_arg *arg) |
|
{ |
|
int status; |
|
|
|
- if (key == Qundef) return ST_CONTINUE; |
|
status = (*arg->func)(key, value, arg->arg); |
|
if (status == ST_CONTINUE) { |
|
return ST_CHECK; |
|
@@ -140,7 +131,7 @@ st_foreach_safe(st_table *table, int (*func)(ANYARGS), st_data_t a) |
|
arg.tbl = table; |
|
arg.func = (st_foreach_func *)func; |
|
arg.arg = a; |
|
- if (st_foreach(table, foreach_safe_i, (st_data_t)&arg)) { |
|
+ if (st_foreach_check(table, foreach_safe_i, (st_data_t)&arg, 0)) { |
|
rb_raise(rb_eRuntimeError, "hash modified during iteration"); |
|
} |
|
} |
|
@@ -154,21 +145,21 @@ struct hash_foreach_arg { |
|
}; |
|
|
|
static int |
|
-hash_foreach_iter(st_data_t key, st_data_t value, struct hash_foreach_arg *arg) |
|
+hash_foreach_iter(st_data_t key, st_data_t value, st_data_t argp) |
|
{ |
|
+ struct hash_foreach_arg *arg = (struct hash_foreach_arg *)argp; |
|
int status; |
|
st_table *tbl; |
|
|
|
tbl = RHASH(arg->hash)->ntbl; |
|
- if ((VALUE)key == Qundef) return ST_CONTINUE; |
|
status = (*arg->func)((VALUE)key, (VALUE)value, arg->arg); |
|
if (RHASH(arg->hash)->ntbl != tbl) { |
|
rb_raise(rb_eRuntimeError, "rehash occurred during iteration"); |
|
} |
|
switch (status) { |
|
case ST_DELETE: |
|
- st_delete_safe(tbl, &key, 0, Qundef); |
|
FL_SET(arg->hash, HASH_DELETED); |
|
+ return ST_DELETE; |
|
case ST_CONTINUE: |
|
break; |
|
case ST_STOP: |
|
@@ -184,7 +175,7 @@ hash_foreach_ensure(VALUE hash) |
|
|
|
if (RHASH(hash)->iter_lev == 0) { |
|
if (FL_TEST(hash, HASH_DELETED)) { |
|
- st_cleanup_safe(RHASH(hash)->ntbl, Qundef); |
|
+ st_cleanup_safe(RHASH(hash)->ntbl, (st_data_t)Qundef); |
|
FL_UNSET(hash, HASH_DELETED); |
|
} |
|
} |
|
@@ -192,9 +183,10 @@ hash_foreach_ensure(VALUE hash) |
|
} |
|
|
|
static VALUE |
|
-hash_foreach_call(struct hash_foreach_arg *arg) |
|
+hash_foreach_call(VALUE arg) |
|
{ |
|
- if (st_foreach(RHASH(arg->hash)->ntbl, hash_foreach_iter, (st_data_t)arg)) { |
|
+ VALUE hash = ((struct hash_foreach_arg *)arg)->hash; |
|
+ if (st_foreach_check(RHASH(hash)->ntbl, hash_foreach_iter, (st_data_t)arg, (st_data_t)Qundef)) { |
|
rb_raise(rb_eRuntimeError, "hash modified during iteration"); |
|
} |
|
return Qnil; |
|
@@ -447,7 +439,7 @@ rb_hash_rehash_i(VALUE key, VALUE value, VALUE arg) |
|
{ |
|
st_table *tbl = (st_table *)arg; |
|
|
|
- if (key != Qundef) st_insert(tbl, key, value); |
|
+ st_insert(tbl, (st_data_t)key, (st_data_t)value); |
|
return ST_CONTINUE; |
|
} |
|
|
|
@@ -490,6 +482,20 @@ rb_hash_rehash(VALUE hash) |
|
return hash; |
|
} |
|
|
|
+static VALUE |
|
+hash_default_value(VALUE hash, VALUE key) |
|
+{ |
|
+ if (rb_method_basic_definition_p(CLASS_OF(hash), id_default)) { |
|
+ VALUE ifnone = RHASH_IFNONE(hash); |
|
+ if (!FL_TEST(hash, HASH_PROC_DEFAULT)) return ifnone; |
|
+ if (key == Qundef) return Qnil; |
|
+ return rb_funcall(ifnone, id_yield, 2, hash, key); |
|
+ } |
|
+ else { |
|
+ return rb_funcall(hash, id_default, 1, key); |
|
+ } |
|
+} |
|
+ |
|
/* |
|
* call-seq: |
|
* hsh[key] -> value |
|
@@ -510,13 +516,7 @@ rb_hash_aref(VALUE hash, VALUE key) |
|
st_data_t val; |
|
|
|
if (!RHASH(hash)->ntbl || !st_lookup(RHASH(hash)->ntbl, key, &val)) { |
|
- if (!FL_TEST(hash, HASH_PROC_DEFAULT) && |
|
- rb_method_basic_definition_p(CLASS_OF(hash), id_default)) { |
|
- return RHASH_IFNONE(hash); |
|
- } |
|
- else { |
|
- return rb_funcall(hash, id_default, 1, key); |
|
- } |
|
+ return hash_default_value(hash, key); |
|
} |
|
return (VALUE)val; |
|
} |
|
@@ -659,7 +659,7 @@ rb_hash_default(int argc, VALUE *argv, VALUE hash) |
|
static VALUE |
|
rb_hash_set_default(VALUE hash, VALUE ifnone) |
|
{ |
|
- rb_hash_modify(hash); |
|
+ rb_hash_modify_check(hash); |
|
RHASH_IFNONE(hash) = ifnone; |
|
FL_UNSET(hash, HASH_PROC_DEFAULT); |
|
return ifnone; |
|
@@ -707,7 +707,7 @@ rb_hash_set_default_proc(VALUE hash, VALUE proc) |
|
{ |
|
VALUE b; |
|
|
|
- rb_hash_modify(hash); |
|
+ rb_hash_modify_check(hash); |
|
b = rb_check_convert_type(proc, T_DATA, "Proc", "to_proc"); |
|
if (NIL_P(b) || !rb_obj_is_proc(b)) { |
|
rb_raise(rb_eTypeError, |
|
@@ -776,7 +776,7 @@ rb_hash_delete_key(VALUE hash, VALUE key) |
|
if (!RHASH(hash)->ntbl) |
|
return Qundef; |
|
if (RHASH(hash)->iter_lev > 0) { |
|
- if (st_delete_safe(RHASH(hash)->ntbl, &ktmp, &val, Qundef)) { |
|
+ if (st_delete_safe(RHASH(hash)->ntbl, &ktmp, &val, (st_data_t)Qundef)) { |
|
FL_SET(hash, HASH_DELETED); |
|
return (VALUE)val; |
|
} |
|
@@ -809,7 +809,7 @@ rb_hash_delete(VALUE hash, VALUE key) |
|
{ |
|
VALUE val; |
|
|
|
- rb_hash_modify(hash); |
|
+ rb_hash_modify_check(hash); |
|
val = rb_hash_delete_key(hash, key); |
|
if (val != Qundef) return val; |
|
if (rb_block_given_p()) { |
|
@@ -828,7 +828,6 @@ shift_i(VALUE key, VALUE value, VALUE arg) |
|
{ |
|
struct shift_var *var = (struct shift_var *)arg; |
|
|
|
- if (key == Qundef) return ST_CONTINUE; |
|
if (var->key != Qundef) return ST_STOP; |
|
var->key = key; |
|
var->val = value; |
|
@@ -840,7 +839,6 @@ shift_i_safe(VALUE key, VALUE value, VALUE arg) |
|
{ |
|
struct shift_var *var = (struct shift_var *)arg; |
|
|
|
- if (key == Qundef) return ST_CONTINUE; |
|
var->key = key; |
|
var->val = value; |
|
return ST_STOP; |
|
@@ -864,29 +862,25 @@ rb_hash_shift(VALUE hash) |
|
{ |
|
struct shift_var var; |
|
|
|
- rb_hash_modify(hash); |
|
- var.key = Qundef; |
|
- rb_hash_foreach(hash, RHASH(hash)->iter_lev > 0 ? shift_i_safe : shift_i, |
|
- (VALUE)&var); |
|
- |
|
- if (var.key != Qundef) { |
|
- if (RHASH(hash)->iter_lev > 0) { |
|
- rb_hash_delete_key(hash, var.key); |
|
+ rb_hash_modify_check(hash); |
|
+ if (RHASH(hash)->ntbl) { |
|
+ var.key = Qundef; |
|
+ rb_hash_foreach(hash, RHASH(hash)->iter_lev > 0 ? shift_i_safe : shift_i, |
|
+ (VALUE)&var); |
|
+ |
|
+ if (var.key != Qundef) { |
|
+ if (RHASH(hash)->iter_lev > 0) { |
|
+ rb_hash_delete_key(hash, var.key); |
|
+ } |
|
+ return rb_assoc_new(var.key, var.val); |
|
} |
|
- return rb_assoc_new(var.key, var.val); |
|
- } |
|
- else if (FL_TEST(hash, HASH_PROC_DEFAULT)) { |
|
- return rb_funcall(RHASH_IFNONE(hash), id_yield, 2, hash, Qnil); |
|
- } |
|
- else { |
|
- return RHASH_IFNONE(hash); |
|
} |
|
+ return hash_default_value(hash, Qnil); |
|
} |
|
|
|
static int |
|
delete_if_i(VALUE key, VALUE value, VALUE hash) |
|
{ |
|
- if (key == Qundef) return ST_CONTINUE; |
|
if (RTEST(rb_yield_values(2, key, value))) { |
|
rb_hash_delete_key(hash, key); |
|
} |
|
@@ -912,8 +906,9 @@ VALUE |
|
rb_hash_delete_if(VALUE hash) |
|
{ |
|
RETURN_ENUMERATOR(hash, 0, 0); |
|
- rb_hash_modify(hash); |
|
- rb_hash_foreach(hash, delete_if_i, hash); |
|
+ rb_hash_modify_check(hash); |
|
+ if (RHASH(hash)->ntbl) |
|
+ rb_hash_foreach(hash, delete_if_i, hash); |
|
return hash; |
|
} |
|
|
|
@@ -984,7 +979,6 @@ rb_hash_values_at(int argc, VALUE *argv, VALUE hash) |
|
static int |
|
select_i(VALUE key, VALUE value, VALUE result) |
|
{ |
|
- if (key == Qundef) return ST_CONTINUE; |
|
if (RTEST(rb_yield_values(2, key, value))) |
|
rb_hash_aset(result, key, value); |
|
return ST_CONTINUE; |
|
@@ -1018,7 +1012,6 @@ rb_hash_select(VALUE hash) |
|
static int |
|
keep_if_i(VALUE key, VALUE value, VALUE hash) |
|
{ |
|
- if (key == Qundef) return ST_CONTINUE; |
|
if (!RTEST(rb_yield_values(2, key, value))) { |
|
return ST_DELETE; |
|
} |
|
@@ -1040,7 +1033,7 @@ rb_hash_select_bang(VALUE hash) |
|
st_index_t n; |
|
|
|
RETURN_ENUMERATOR(hash, 0, 0); |
|
- rb_hash_modify(hash); |
|
+ rb_hash_modify_check(hash); |
|
if (!RHASH(hash)->ntbl) |
|
return Qnil; |
|
n = RHASH(hash)->ntbl->num_entries; |
|
@@ -1065,8 +1058,9 @@ VALUE |
|
rb_hash_keep_if(VALUE hash) |
|
{ |
|
RETURN_ENUMERATOR(hash, 0, 0); |
|
- rb_hash_modify(hash); |
|
- rb_hash_foreach(hash, keep_if_i, hash); |
|
+ rb_hash_modify_check(hash); |
|
+ if (RHASH(hash)->ntbl) |
|
+ rb_hash_foreach(hash, keep_if_i, hash); |
|
return hash; |
|
} |
|
|
|
@@ -1144,9 +1138,7 @@ rb_hash_aset(VALUE hash, VALUE key, VALUE val) |
|
static int |
|
replace_i(VALUE key, VALUE val, VALUE hash) |
|
{ |
|
- if (key != Qundef) { |
|
- rb_hash_aset(hash, key, val); |
|
- } |
|
+ rb_hash_aset(hash, key, val); |
|
|
|
return ST_CONTINUE; |
|
} |
|
@@ -1227,7 +1219,6 @@ rb_hash_empty_p(VALUE hash) |
|
static int |
|
each_value_i(VALUE key, VALUE value) |
|
{ |
|
- if (key == Qundef) return ST_CONTINUE; |
|
rb_yield(value); |
|
return ST_CONTINUE; |
|
} |
|
@@ -1262,7 +1253,6 @@ rb_hash_each_value(VALUE hash) |
|
static int |
|
each_key_i(VALUE key, VALUE value) |
|
{ |
|
- if (key == Qundef) return ST_CONTINUE; |
|
rb_yield(key); |
|
return ST_CONTINUE; |
|
} |
|
@@ -1296,7 +1286,6 @@ rb_hash_each_key(VALUE hash) |
|
static int |
|
each_pair_i(VALUE key, VALUE value) |
|
{ |
|
- if (key == Qundef) return ST_CONTINUE; |
|
rb_yield(rb_assoc_new(key, value)); |
|
return ST_CONTINUE; |
|
} |
|
@@ -1334,7 +1323,6 @@ rb_hash_each_pair(VALUE hash) |
|
static int |
|
to_a_i(VALUE key, VALUE value, VALUE ary) |
|
{ |
|
- if (key == Qundef) return ST_CONTINUE; |
|
rb_ary_push(ary, rb_assoc_new(key, value)); |
|
return ST_CONTINUE; |
|
} |
|
@@ -1367,7 +1355,6 @@ inspect_i(VALUE key, VALUE value, VALUE str) |
|
{ |
|
VALUE str2; |
|
|
|
- if (key == Qundef) return ST_CONTINUE; |
|
str2 = rb_inspect(key); |
|
if (RSTRING_LEN(str) > 1) { |
|
rb_str_cat2(str, ", "); |
|
@@ -1434,7 +1421,6 @@ rb_hash_to_hash(VALUE hash) |
|
static int |
|
keys_i(VALUE key, VALUE value, VALUE ary) |
|
{ |
|
- if (key == Qundef) return ST_CONTINUE; |
|
rb_ary_push(ary, key); |
|
return ST_CONTINUE; |
|
} |
|
@@ -1465,7 +1451,6 @@ rb_hash_keys(VALUE hash) |
|
static int |
|
values_i(VALUE key, VALUE value, VALUE ary) |
|
{ |
|
- if (key == Qundef) return ST_CONTINUE; |
|
rb_ary_push(ary, value); |
|
return ST_CONTINUE; |
|
} |
|
@@ -1524,7 +1509,6 @@ rb_hash_search_value(VALUE key, VALUE value, VALUE arg) |
|
{ |
|
VALUE *data = (VALUE *)arg; |
|
|
|
- if (key == Qundef) return ST_CONTINUE; |
|
if (rb_equal(value, data[1])) { |
|
data[0] = Qtrue; |
|
return ST_STOP; |
|
@@ -1568,7 +1552,6 @@ eql_i(VALUE key, VALUE val1, VALUE arg) |
|
struct equal_data *data = (struct equal_data *)arg; |
|
st_data_t val2; |
|
|
|
- if (key == Qundef) return ST_CONTINUE; |
|
if (!st_lookup(data->tbl, key, &val2)) { |
|
data->result = Qfalse; |
|
return ST_STOP; |
|
@@ -1599,7 +1582,7 @@ hash_equal(VALUE hash1, VALUE hash2, int eql) |
|
struct equal_data data; |
|
|
|
if (hash1 == hash2) return Qtrue; |
|
- if (TYPE(hash2) != T_HASH) { |
|
+ if (!RB_TYPE_P(hash2, T_HASH)) { |
|
if (!rb_respond_to(hash2, rb_intern("to_hash"))) { |
|
return Qfalse; |
|
} |
|
@@ -1670,7 +1653,6 @@ hash_i(VALUE key, VALUE val, VALUE arg) |
|
st_index_t *hval = (st_index_t *)arg; |
|
st_index_t hdata[2]; |
|
|
|
- if (key == Qundef) return ST_CONTINUE; |
|
hdata[0] = rb_hash(key); |
|
hdata[1] = rb_hash(val); |
|
*hval ^= st_hash(hdata, sizeof(hdata), 0); |
|
@@ -1711,7 +1693,6 @@ rb_hash_hash(VALUE hash) |
|
static int |
|
rb_hash_invert_i(VALUE key, VALUE value, VALUE hash) |
|
{ |
|
- if (key == Qundef) return ST_CONTINUE; |
|
rb_hash_aset(hash, value, key); |
|
return ST_CONTINUE; |
|
} |
|
@@ -1740,7 +1721,6 @@ rb_hash_invert(VALUE hash) |
|
static int |
|
rb_hash_update_i(VALUE key, VALUE value, VALUE hash) |
|
{ |
|
- if (key == Qundef) return ST_CONTINUE; |
|
hash_update(hash, key); |
|
st_insert(RHASH(hash)->ntbl, key, value); |
|
return ST_CONTINUE; |
|
@@ -1749,7 +1729,6 @@ rb_hash_update_i(VALUE key, VALUE value, VALUE hash) |
|
static int |
|
rb_hash_update_block_i(VALUE key, VALUE value, VALUE hash) |
|
{ |
|
- if (key == Qundef) return ST_CONTINUE; |
|
if (rb_hash_has_key(hash, key)) { |
|
value = rb_yield_values(3, key, rb_hash_aref(hash, key), value); |
|
} |
|
@@ -1806,7 +1785,6 @@ rb_hash_update_func_i(VALUE key, VALUE value, VALUE arg0) |
|
struct update_arg *arg = (struct update_arg *)arg0; |
|
VALUE hash = arg->hash; |
|
|
|
- if (key == Qundef) return ST_CONTINUE; |
|
if (rb_hash_has_key(hash, key)) { |
|
value = (*arg->func)(key, rb_hash_aref(hash, key), value); |
|
} |
|
@@ -1863,7 +1841,6 @@ assoc_i(VALUE key, VALUE val, VALUE arg) |
|
{ |
|
VALUE *args = (VALUE *)arg; |
|
|
|
- if (key == Qundef) return ST_CONTINUE; |
|
if (RTEST(rb_equal(args[0], key))) { |
|
args[1] = rb_assoc_new(key, val); |
|
return ST_STOP; |
|
@@ -1901,7 +1878,6 @@ rassoc_i(VALUE key, VALUE val, VALUE arg) |
|
{ |
|
VALUE *args = (VALUE *)arg; |
|
|
|
- if (key == Qundef) return ST_CONTINUE; |
|
if (RTEST(rb_equal(args[0], val))) { |
|
args[1] = rb_assoc_new(key, val); |
|
return ST_STOP; |
|
@@ -2198,7 +2174,7 @@ rb_env_path_tainted(void) |
|
} |
|
|
|
#if defined(_WIN32) || (defined(HAVE_SETENV) && defined(HAVE_UNSETENV)) |
|
-#elif defined __sun__ |
|
+#elif defined __sun |
|
static int |
|
in_origenv(const char *str) |
|
{ |
|
@@ -2286,7 +2262,7 @@ ruby_setenv(const char *name, const char *value) |
|
rb_sys_fail("unsetenv"); |
|
#endif |
|
} |
|
-#elif defined __sun__ |
|
+#elif defined __sun |
|
size_t len; |
|
char **env_ptr, *str; |
|
if (strchr(name, '=')) { |
|
@@ -3084,11 +3060,9 @@ env_invert(void) |
|
static int |
|
env_replace_i(VALUE key, VALUE val, VALUE keys) |
|
{ |
|
- if (key != Qundef) { |
|
- env_aset(Qnil, key, val); |
|
- if (rb_ary_includes(keys, key)) { |
|
- rb_ary_delete(keys, key); |
|
- } |
|
+ env_aset(Qnil, key, val); |
|
+ if (rb_ary_includes(keys, key)) { |
|
+ rb_ary_delete(keys, key); |
|
} |
|
return ST_CONTINUE; |
|
} |
|
@@ -3120,12 +3094,10 @@ env_replace(VALUE env, VALUE hash) |
|
static int |
|
env_update_i(VALUE key, VALUE val) |
|
{ |
|
- if (key != Qundef) { |
|
- if (rb_block_given_p()) { |
|
- val = rb_yield_values(3, key, rb_f_getenv(Qnil, key), val); |
|
- } |
|
- env_aset(Qnil, key, val); |
|
+ if (rb_block_given_p()) { |
|
+ val = rb_yield_values(3, key, rb_f_getenv(Qnil, key), val); |
|
} |
|
+ env_aset(Qnil, key, val); |
|
return ST_CONTINUE; |
|
} |
|
|
|
@@ -3150,15 +3122,116 @@ env_update(VALUE env, VALUE hash) |
|
} |
|
|
|
/* |
|
- * A <code>Hash</code> is a collection of key-value pairs. It is |
|
- * similar to an <code>Array</code>, except that indexing is done via |
|
- * arbitrary keys of any object type, not an integer index. Hashes enumerate |
|
- * their values in the order that the corresponding keys were inserted. |
|
+ * A Hash is a dictionary-like collection of unique keys and their values. |
|
+ * Also called associative arrays, they are similar to Arrays, but where an |
|
+ * Array uses integers as its index, a Hash allows you to use any object |
|
+ * type. |
|
+ * |
|
+ * Hashes enumerate their values in the order that the corresponding keys |
|
+ * were inserted. |
|
+ * |
|
+ * A Hash can be easily created by using its implicit form: |
|
+ * |
|
+ * grades = { "Jane Doe" => 10, "Jim Doe" => 6 } |
|
+ * |
|
+ * Hashes allow an alternate syntax form when your keys are always symbols. |
|
+ * Instead of |
|
+ * |
|
+ * options = { :font_size => 10, :font_family => "Arial" } |
|
+ * |
|
+ * You could write it as: |
|
+ * |
|
+ * options = { font_size: 10, font_family: "Arial" } |
|
+ * |
|
+ * Each named key is a symbol you can access in hash: |
|
+ * |
|
+ * options[:font_size] # => 10 |
|
+ * |
|
+ * A Hash can also be created through its ::new method: |
|
+ * |
|
+ * grades = Hash.new |
|
+ * grades["Dorothy Doe"] = 9 |
|
* |
|
* Hashes have a <em>default value</em> that is returned when accessing |
|
- * keys that do not exist in the hash. By default, that value is |
|
- * <code>nil</code>. |
|
+ * keys that do not exist in the hash. If no default is set +nil+ is used. |
|
+ * You can set the default value by sending it as an argument to Hash.new: |
|
+ * |
|
+ * grades = Hash.new(0) |
|
+ * |
|
+ * Or by using the #default= method: |
|
+ * |
|
+ * grades = {"Timmy Doe" => 8} |
|
+ * grades.default = 0 |
|
+ * |
|
+ * Accessing a value in a Hash requires using its key: |
|
+ * |
|
+ * puts grades["Jane Doe"] # => 10 |
|
+ * |
|
+ * === Common Uses |
|
+ * |
|
+ * Hashes are an easy way to represent data structures, such as |
|
+ * |
|
+ * books = {} |
|
+ * books[:matz] = "The Ruby Language" |
|
+ * books[:black] = "The Well-Grounded Rubyist" |
|
+ * |
|
+ * Hashes are also commonly used as a way to have named parameters in |
|
+ * functions. Note that no brackets are used below. If a hash is the last |
|
+ * argument on a method call, no braces are needed, thus creating a really |
|
+ * clean interface: |
|
+ * |
|
+ * Person.create(name: "John Doe", age: 27) |
|
+ * |
|
+ * def self.create(params) |
|
+ * @name = params[:name] |
|
+ * @age = params[:age] |
|
+ * end |
|
+ * |
|
+ * === Hash Keys |
|
+ * |
|
+ * Two objects refer to the same hash key when their <code>hash</code> value |
|
+ * is identical and the two objects are <code>eql?</code> to each other. |
|
+ * |
|
+ * A user-defined class may be used as a hash key if the <code>hash</code> |
|
+ * and <code>eql?</code> methods are overridden to provide meaningful |
|
+ * behavior. By default, separate instances refer to separate hash keys. |
|
+ * |
|
+ * A typical implementation of <code>hash</code> is based on the |
|
+ * object's data while <code>eql?</code> is usually aliased to the overridden |
|
+ * <code>==</code> method: |
|
+ * |
|
+ * class Book |
|
+ * attr_reader :author, :title |
|
+ * |
|
+ * def initialize(author, title) |
|
+ * @author = author |
|
+ * @title = title |
|
+ * end |
|
+ * |
|
+ * def ==(other) |
|
+ * self.class === other and |
|
+ * other.author == @author and |
|
+ * other.title == @title |
|
+ * end |
|
+ * |
|
+ * alias eql? == |
|
+ * |
|
+ * def hash |
|
+ * @author.hash ^ @title.hash # XOR |
|
+ * end |
|
+ * end |
|
+ * |
|
+ * book1 = Book.new 'matz', 'Ruby in a Nutshell' |
|
+ * book2 = Book.new 'matz', 'Ruby in a Nutshell' |
|
+ * |
|
+ * reviews = {} |
|
+ * |
|
+ * reviews[book1] = 'Great reference!' |
|
+ * reviews[book2] = 'Nice and compact!' |
|
+ * |
|
+ * reviews.length #=> 1 |
|
* |
|
+ * See also Object#hash and Object#eql? |
|
*/ |
|
|
|
void |
|
diff --git a/include/ruby/intern.h b/include/ruby/intern.h |
|
index 6dec838..9b5157f 100644 |
|
--- a/include/ruby/intern.h |
|
+++ b/include/ruby/intern.h |
|
@@ -419,6 +419,7 @@ size_t ruby_stack_length(VALUE**); |
|
int rb_during_gc(void); |
|
void rb_gc_mark_locations(VALUE*, VALUE*); |
|
void rb_mark_tbl(struct st_table*); |
|
+void rb_mark_sa_tbl(sa_table*); |
|
void rb_mark_set(struct st_table*); |
|
void rb_mark_hash(struct st_table*); |
|
void rb_gc_mark_maybe(VALUE); |
|
@@ -849,7 +850,7 @@ VALUE rb_f_trace_var(int, VALUE*); |
|
VALUE rb_f_untrace_var(int, VALUE*); |
|
VALUE rb_f_global_variables(void); |
|
void rb_alias_variable(ID, ID); |
|
-struct st_table* rb_generic_ivar_table(VALUE); |
|
+sa_table* rb_generic_ivar_table(VALUE); |
|
void rb_copy_generic_ivar(VALUE,VALUE); |
|
void rb_mark_generic_ivar(VALUE); |
|
void rb_mark_generic_ivar_tbl(void); |
|
diff --git a/include/ruby/ruby.h b/include/ruby/ruby.h |
|
index 2f97b33..1c84e14 100644 |
|
--- a/include/ruby/ruby.h |
|
+++ b/include/ruby/ruby.h |
|
@@ -605,7 +605,7 @@ struct RObject { |
|
struct { |
|
long numiv; |
|
VALUE *ivptr; |
|
- struct st_table *iv_index_tbl; /* shortcut for RCLASS_IV_INDEX_TBL(rb_obj_class(obj)) */ |
|
+ struct sa_table *iv_index_tbl; /* shortcut for RCLASS_IV_INDEX_TBL(rb_obj_class(obj)) */ |
|
} heap; |
|
VALUE ary[ROBJECT_EMBED_LEN_MAX]; |
|
} as; |
|
@@ -626,12 +626,13 @@ struct RObject { |
|
|
|
/** @internal */ |
|
typedef struct rb_classext_struct rb_classext_t; |
|
+typedef struct rb_class_cache_struct rb_class_cache_t; |
|
|
|
struct RClass { |
|
struct RBasic basic; |
|
+ VALUE super; |
|
rb_classext_t *ptr; |
|
- struct st_table *m_tbl; |
|
- struct st_table *iv_index_tbl; |
|
+ rb_class_cache_t *cache; |
|
}; |
|
#define RCLASS_SUPER(c) rb_class_get_superclass(c) |
|
#define RMODULE_IV_TBL(m) RCLASS_IV_TBL(m) |
|
diff --git a/include/ruby/st.h b/include/ruby/st.h |
|
index 50f2a75..aff94fc 100644 |
|
--- a/include/ruby/st.h |
|
+++ b/include/ruby/st.h |
|
@@ -36,7 +36,7 @@ typedef unsigned long st_data_t; |
|
#elif SIZEOF_LONG_LONG == SIZEOF_VOIDP |
|
typedef unsigned LONG_LONG st_data_t; |
|
#else |
|
-# error ---->> st.c requires sizeof(void*) == sizeof(long) to be compiled. <<---- |
|
+# error ---->> st.c requires sizeof(void*) == sizeof(long) or sizeof(LONG_LONG) to be compiled. <<---- |
|
#endif |
|
#define ST_DATA_T_DEFINED |
|
|
|
@@ -74,6 +74,11 @@ struct st_hash_type { |
|
|
|
#define ST_INDEX_BITS (sizeof(st_index_t) * CHAR_BIT) |
|
|
|
+typedef struct st_packed_entry { |
|
+ st_index_t hash; |
|
+ st_data_t key, val; |
|
+} st_packed_entry; |
|
+ |
|
struct st_table { |
|
const struct st_hash_type *type; |
|
st_index_t num_bins; |
|
@@ -91,8 +96,17 @@ struct st_table { |
|
__extension__ |
|
#endif |
|
st_index_t num_entries : ST_INDEX_BITS - 1; |
|
- struct st_table_entry **bins; |
|
- struct st_table_entry *head, *tail; |
|
+ union { |
|
+ struct { |
|
+ struct st_table_entry **bins; |
|
+ struct st_table_entry *head, *tail; |
|
+ } big; |
|
+ struct { |
|
+ struct st_packed_entry *entries; |
|
+ st_index_t real_entries; |
|
+ } packed; |
|
+ st_packed_entry upacked; |
|
+ } as; |
|
}; |
|
|
|
#define st_is_member(table,key) st_lookup((table),(key),(st_data_t *)0) |
|
@@ -114,6 +128,7 @@ int st_insert2(st_table *, st_data_t, st_data_t, st_data_t (*)(st_data_t)); |
|
int st_lookup(st_table *, st_data_t, st_data_t *); |
|
int st_get_key(st_table *, st_data_t, st_data_t *); |
|
int st_foreach(st_table *, int (*)(ANYARGS), st_data_t); |
|
+int st_foreach_check(st_table *, int (*)(ANYARGS), st_data_t, st_data_t); |
|
int st_reverse_foreach(st_table *, int (*)(ANYARGS), st_data_t); |
|
void st_add_direct(st_table *, st_data_t, st_data_t); |
|
void st_free_table(st_table *); |
|
@@ -136,6 +151,51 @@ st_index_t st_hash_start(st_index_t h); |
|
#pragma GCC visibility pop |
|
#endif |
|
|
|
+typedef unsigned int sa_index_t; |
|
+#define SA_STOP ST_STOP |
|
+#define SA_CONTINUE ST_CONTINUE |
|
+ |
|
+#define SA_EMPTY 0 |
|
+ |
|
+typedef struct sa_entry { |
|
+ sa_index_t next; |
|
+ sa_index_t key; |
|
+ st_data_t value; |
|
+} sa_entry; |
|
+ |
|
+typedef struct sa_table { |
|
+ sa_index_t num_bins; |
|
+ sa_index_t num_entries; |
|
+ sa_index_t free_pos; |
|
+ sa_entry *entries; |
|
+} sa_table; |
|
+ |
|
+#define SA_EMPTY_TABLE {0, 0, 0, 0}; |
|
+void sa_init_table(sa_table *, sa_index_t); |
|
+sa_table *sa_new_table(); |
|
+int sa_insert(sa_table *, sa_index_t, st_data_t); |
|
+int sa_lookup(sa_table *, sa_index_t, st_data_t *); |
|
+int sa_delete(sa_table *, sa_index_t, st_data_t *); |
|
+void sa_clear(sa_table *); |
|
+void sa_clear_no_free(sa_table *); |
|
+void sa_free_table(sa_table *); |
|
+int sa_foreach(sa_table *, int (*)(ANYARGS), st_data_t); |
|
+size_t sa_memsize(const sa_table *); |
|
+sa_table *sa_copy(sa_table*); |
|
+void sa_copy_to(sa_table*, sa_table*); |
|
+typedef int (*sa_iter_func)(sa_index_t key, st_data_t val, st_data_t arg); |
|
+ |
|
+#define SA_FOREACH_START_I(table, entry) do { \ |
|
+ sa_table *T##entry = (table); \ |
|
+ sa_index_t K##entry; \ |
|
+ for(K##entry = 0; K##entry < T##entry->num_bins; K##entry++) { \ |
|
+ sa_entry *entry = T##entry->entries + K##entry; \ |
|
+ if (entry->next != SA_EMPTY) { \ |
|
+ st_data_t value = entry->value |
|
+#define SA_FOREACH_END() } } } while(0) |
|
+ |
|
+#define SA_FOREACH_START(table) SA_FOREACH_START_I(table, entry) |
|
+ |
|
#if defined(__cplusplus) |
|
#if 0 |
|
{ /* satisfy cc-mode */ |
|
diff --git a/internal.h b/internal.h |
|
index 5d0cff0..ec72a1b 100644 |
|
--- a/internal.h |
|
+++ b/internal.h |
|
@@ -24,18 +24,24 @@ struct rb_deprecated_classext_struct { |
|
}; |
|
|
|
struct rb_classext_struct { |
|
- VALUE super; |
|
- struct st_table *iv_tbl; |
|
- struct st_table *const_tbl; |
|
+ sa_table m_tbl; |
|
+ sa_table iv_tbl; |
|
+ sa_table const_tbl; |
|
+ sa_table iv_index_tbl; |
|
+}; |
|
+ |
|
+struct rb_class_cache_struct { |
|
+ VALUE method_cache_version; |
|
+ sa_table m_cache_tbl; |
|
}; |
|
|
|
#undef RCLASS_SUPER |
|
#define RCLASS_EXT(c) (RCLASS(c)->ptr) |
|
-#define RCLASS_SUPER(c) (RCLASS_EXT(c)->super) |
|
-#define RCLASS_IV_TBL(c) (RCLASS_EXT(c)->iv_tbl) |
|
-#define RCLASS_CONST_TBL(c) (RCLASS_EXT(c)->const_tbl) |
|
-#define RCLASS_M_TBL(c) (RCLASS(c)->m_tbl) |
|
-#define RCLASS_IV_INDEX_TBL(c) (RCLASS(c)->iv_index_tbl) |
|
+#define RCLASS_SUPER(c) (RCLASS(c)->super) |
|
+#define RCLASS_IV_TBL(c) (&RCLASS_EXT(c)->iv_tbl) |
|
+#define RCLASS_CONST_TBL(c) (&RCLASS_EXT(c)->const_tbl) |
|
+#define RCLASS_M_TBL(c) (&RCLASS_EXT(c)->m_tbl) |
|
+#define RCLASS_IV_INDEX_TBL(c) (&RCLASS_EXT(c)->iv_index_tbl) |
|
|
|
struct vtm; /* defined by timev.h */ |
|
|
|
diff --git a/marshal.c b/marshal.c |
|
index 9a43cdb..a4a3551 100644 |
|
--- a/marshal.c |
|
+++ b/marshal.c |
|
@@ -506,7 +506,7 @@ w_uclass(VALUE obj, VALUE super, struct dump_arg *arg) |
|
} |
|
|
|
static int |
|
-w_obj_each(ID id, VALUE value, struct dump_call_arg *arg) |
|
+w_obj_each(sa_index_t id, VALUE value, struct dump_call_arg *arg) |
|
{ |
|
if (id == rb_id_encoding()) return ST_CONTINUE; |
|
if (id == rb_intern("E")) return ST_CONTINUE; |
|
@@ -553,13 +553,13 @@ w_encoding(VALUE obj, long num, struct dump_call_arg *arg) |
|
} |
|
|
|
static void |
|
-w_ivar(VALUE obj, st_table *tbl, struct dump_call_arg *arg) |
|
+w_ivar(VALUE obj, sa_table *tbl, struct dump_call_arg *arg) |
|
{ |
|
long num = tbl ? tbl->num_entries : 0; |
|
|
|
w_encoding(obj, num, arg); |
|
if (tbl) { |
|
- st_foreach_safe(tbl, w_obj_each, (st_data_t)arg); |
|
+ sa_foreach(tbl, w_obj_each, (st_data_t)arg); |
|
} |
|
} |
|
|
|
@@ -586,7 +586,7 @@ static void |
|
w_object(VALUE obj, struct dump_arg *arg, int limit) |
|
{ |
|
struct dump_call_arg c_arg; |
|
- st_table *ivtbl = 0; |
|
+ sa_table *ivtbl = 0; |
|
st_data_t num; |
|
int hasiv = 0; |
|
#define has_ivars(obj, ivtbl) (((ivtbl) = rb_generic_ivar_table(obj)) != 0 || \ |
|
@@ -651,7 +651,7 @@ w_object(VALUE obj, struct dump_arg *arg, int limit) |
|
} |
|
if (rb_respond_to(obj, s_dump)) { |
|
VALUE v; |
|
- st_table *ivtbl2 = 0; |
|
+ sa_table *ivtbl2 = 0; |
|
int hasiv2; |
|
|
|
v = rb_funcall(obj, s_dump, 1, INT2NUM(limit)); |
|
diff --git a/method.h b/method.h |
|
index 9229896..2fecd57 100644 |
|
--- a/method.h |
|
+++ b/method.h |
|
@@ -100,6 +100,6 @@ int rb_method_entry_eq(const rb_method_entry_t *m1, const rb_method_entry_t *m2) |
|
void rb_mark_method_entry(const rb_method_entry_t *me); |
|
void rb_free_method_entry(rb_method_entry_t *me); |
|
void rb_sweep_method_entry(void *vm); |
|
-void rb_free_m_table(st_table *tbl); |
|
+void rb_free_m_table(sa_table *tbl); |
|
|
|
#endif /* METHOD_H */ |
|
diff --git a/object.c b/object.c |
|
index f45e013..1352d01 100644 |
|
--- a/object.c |
|
+++ b/object.c |
|
@@ -229,17 +229,8 @@ init_copy(VALUE dest, VALUE obj) |
|
break; |
|
case T_CLASS: |
|
case T_MODULE: |
|
- if (RCLASS_IV_TBL(dest)) { |
|
- st_free_table(RCLASS_IV_TBL(dest)); |
|
- RCLASS_IV_TBL(dest) = 0; |
|
- } |
|
- if (RCLASS_CONST_TBL(dest)) { |
|
- rb_free_const_table(RCLASS_CONST_TBL(dest)); |
|
- RCLASS_CONST_TBL(dest) = 0; |
|
- } |
|
- if (RCLASS_IV_TBL(obj)) { |
|
- RCLASS_IV_TBL(dest) = st_copy(RCLASS_IV_TBL(obj)); |
|
- } |
|
+ rb_free_const_table(RCLASS_CONST_TBL(dest)); |
|
+ sa_copy_to(RCLASS_IV_TBL(obj), RCLASS_IV_TBL(dest)); |
|
break; |
|
} |
|
} |
|
@@ -530,7 +521,7 @@ rb_obj_is_kind_of(VALUE obj, VALUE c) |
|
} |
|
|
|
while (cl) { |
|
- if (cl == c || RCLASS_M_TBL(cl) == RCLASS_M_TBL(c)) |
|
+ if (cl == c || RCLASS_EXT(cl) == RCLASS_EXT(c)) |
|
return Qtrue; |
|
cl = RCLASS_SUPER(cl); |
|
} |
|
@@ -1355,13 +1346,13 @@ rb_class_inherited_p(VALUE mod, VALUE arg) |
|
rb_raise(rb_eTypeError, "compared with non class/module"); |
|
} |
|
while (mod) { |
|
- if (RCLASS_M_TBL(mod) == RCLASS_M_TBL(arg)) |
|
+ if (RCLASS_EXT(mod) == RCLASS_EXT(arg)) |
|
return Qtrue; |
|
mod = RCLASS_SUPER(mod); |
|
} |
|
/* not mod < arg; check if mod > arg */ |
|
while (arg) { |
|
- if (RCLASS_M_TBL(arg) == RCLASS_M_TBL(start)) |
|
+ if (RCLASS_EXT(arg) == RCLASS_EXT(start)) |
|
return Qfalse; |
|
arg = RCLASS_SUPER(arg); |
|
} |
|
diff --git a/pool_alloc.h b/pool_alloc.h |
|
new file mode 100644 |
|
index 0000000..957708e |
|
--- /dev/null |
|
+++ b/pool_alloc.h |
|
@@ -0,0 +1,11 @@ |
|
+#ifndef POOL_ALLOC_H |
|
+#define POOL_ALLOC_H |
|
+ |
|
+#define POOL_ALLOC_API |
|
+#ifdef POOL_ALLOC_API |
|
+void ruby_xpool_free(void *ptr); |
|
+void *ruby_xpool_malloc_6p(); |
|
+void *ruby_xpool_malloc_11p(); |
|
+#endif |
|
+ |
|
+#endif |
|
diff --git a/pool_alloc.inc.h b/pool_alloc.inc.h |
|
new file mode 100644 |
|
index 0000000..a7879ab |
|
--- /dev/null |
|
+++ b/pool_alloc.inc.h |
|
@@ -0,0 +1,156 @@ |
|
+/* |
|
+ * this is generic pool allocator |
|
+ * you should define following macroses: |
|
+ * ITEM_NAME - unique identifier, which allows to hold functions in a namespace |
|
+ * ITEM_TYPEDEF(name) - passed to typedef to localize item type |
|
+ * free_entry - desired name of function for free entry |
|
+ * alloc_entry - defired name of function for allocate entry |
|
+ */ |
|
+ |
|
+#if POOL_ALLOC_PART == 1 |
|
+#ifdef HEAP_ALIGN_LOG |
|
+#define DEFAULT_POOL_SIZE (1 << HEAP_ALIGN_LOG) |
|
+#else |
|
+#define DEFAULT_POOL_SIZE (sizeof(void*) * 2048) |
|
+#endif |
|
+typedef unsigned int pool_holder_counter; |
|
+ |
|
+typedef struct pool_entry_list pool_entry_list; |
|
+typedef struct pool_holder pool_holder; |
|
+ |
|
+typedef struct pool_header { |
|
+ pool_holder *first; |
|
+ pool_holder *_black_magick; |
|
+ pool_holder_counter size; // size of entry in sizeof(void*) items |
|
+ pool_holder_counter total; // size of entry in sizeof(void*) items |
|
+} pool_header; |
|
+ |
|
+struct pool_holder { |
|
+ pool_holder_counter free, total; |
|
+ pool_header *header; |
|
+ void *freep; |
|
+ pool_holder *fore, *back; |
|
+ void *data[1]; |
|
+}; |
|
+#define POOL_DATA_SIZE(pool_size) (((pool_size) - sizeof(void*) * 6 - offsetof(pool_holder, data)) / sizeof(void*)) |
|
+#define POOL_ENTRY_SIZE(item_type) ((sizeof(item_type) - 1) / sizeof(void*) + 1) |
|
+#define POOL_HOLDER_COUNT(pool_size, item_type) (POOL_DATA_SIZE(pool_size)/POOL_ENTRY_SIZE(item_type)) |
|
+#define INIT_POOL(item_type) {NULL, NULL, POOL_ENTRY_SIZE(item_type), POOL_HOLDER_COUNT(DEFAULT_POOL_SIZE, item_type)} |
|
+ |
|
+#elif POOL_ALLOC_PART == 2 |
|
+static pool_holder * |
|
+pool_holder_alloc(pool_header *header) |
|
+{ |
|
+ pool_holder *holder; |
|
+ pool_holder_counter i, size, count; |
|
+ register void **ptr; |
|
+ |
|
+ size_t sz = offsetof(pool_holder, data) + |
|
+ header->size * header->total * sizeof(void*); |
|
+#define objspace (&rb_objspace) |
|
+ vm_malloc_prepare(objspace, DEFAULT_POOL_SIZE); |
|
+ if (header->first != NULL) return header->first; |
|
+ TRY_WITH_GC(holder = (pool_holder*) aligned_malloc(DEFAULT_POOL_SIZE, sz)); |
|
+ malloc_increase += DEFAULT_POOL_SIZE; |
|
+#if CALC_EXACT_MALLOC_SIZE |
|
+ objspace->malloc_params.allocated_size += DEFAULT_POOL_SIZE; |
|
+ objspace->malloc_params.allocations++; |
|
+#endif |
|
+#undef objspace |
|
+ |
|
+ size = header->size; |
|
+ count = header->total; |
|
+ holder->free = count; |
|
+ holder->total = count; |
|
+ holder->header = header; |
|
+ holder->fore = NULL; |
|
+ holder->back = NULL; |
|
+ holder->freep = &holder->data; |
|
+ ptr = holder->data; |
|
+ for(i = count - 1; i; i-- ) { |
|
+ ptr = *ptr = ptr + size; |
|
+ } |
|
+ *ptr = NULL; |
|
+ header->first = holder; |
|
+ return holder; |
|
+} |
|
+ |
|
+static inline void |
|
+pool_holder_unchaing(pool_header *header, pool_holder *holder) |
|
+{ |
|
+ register pool_holder *fore = holder->fore, *back = holder->back; |
|
+ holder->fore = NULL; |
|
+ holder->back = NULL; |
|
+ if (fore != NULL) fore->back = back; |
|
+ else header->_black_magick = back; |
|
+ if (back != NULL) back->fore = fore; |
|
+ else header->first = fore; |
|
+} |
|
+ |
|
+static inline pool_holder * |
|
+entry_holder(void **entry) |
|
+{ |
|
+ return (pool_holder*)(((uintptr_t)entry) & ~(DEFAULT_POOL_SIZE - 1)); |
|
+} |
|
+ |
|
+static inline void |
|
+pool_free_entry(void **entry) |
|
+{ |
|
+ pool_holder *holder = entry_holder(entry); |
|
+ pool_header *header = holder->header; |
|
+ |
|
+ if (holder->free++ == 0) { |
|
+ register pool_holder *first = header->first; |
|
+ if (first == NULL) { |
|
+ header->first = holder; |
|
+ } else { |
|
+ holder->back = first; |
|
+ holder->fore = first->fore; |
|
+ first->fore = holder; |
|
+ if (holder->fore) |
|
+ holder->fore->back = holder; |
|
+ else |
|
+ header->_black_magick = holder; |
|
+ } |
|
+ } else if (holder->free == holder->total && header->first != holder ) { |
|
+ pool_holder_unchaing(header, holder); |
|
+ aligned_free(holder); |
|
+#if CALC_EXACT_MALLOC_SIZE |
|
+ rb_objspace.malloc_params.allocated_size -= DEFAULT_POOL_SIZE; |
|
+ rb_objspace.malloc_params.allocations--; |
|
+#endif |
|
+ return; |
|
+ } |
|
+ |
|
+ *entry = holder->freep; |
|
+ holder->freep = entry; |
|
+} |
|
+ |
|
+static inline void* |
|
+pool_alloc_entry(pool_header *header) |
|
+{ |
|
+ pool_holder *holder = header->first; |
|
+ void **result; |
|
+ if (holder == NULL) { |
|
+ holder = pool_holder_alloc(header); |
|
+ } |
|
+ |
|
+ result = holder->freep; |
|
+ holder->freep = *result; |
|
+ |
|
+ if (--holder->free == 0) { |
|
+ pool_holder_unchaing(header, holder); |
|
+ } |
|
+ |
|
+ return result; |
|
+} |
|
+ |
|
+static void |
|
+pool_finalize_header(pool_header *header) |
|
+{ |
|
+ if (header->first) { |
|
+ aligned_free(header->first); |
|
+ header->first = NULL; |
|
+ } |
|
+} |
|
+#endif |
|
diff --git a/sp_ar.c b/sp_ar.c |
|
new file mode 100644 |
|
index 0000000..2ed69bf |
|
--- /dev/null |
|
+++ b/sp_ar.c |
|
@@ -0,0 +1,374 @@ |
|
+/* |
|
+ * sparse array lib |
|
+ * inspired by Lua table |
|
+ * written by Sokolov Yura aka funny_falcon |
|
+ */ |
|
+#ifdef NOT_RUBY |
|
+#include "regint.h" |
|
+#include "st.h" |
|
+#else |
|
+#include "ruby/ruby.h" |
|
+#endif |
|
+ |
|
+#include <stdio.h> |
|
+#ifdef HAVE_STDLIB_H |
|
+#include <stdlib.h> |
|
+#endif |
|
+#include <string.h> |
|
+ |
|
+#ifdef RUBY |
|
+#define malloc xmalloc |
|
+#define calloc xcalloc |
|
+#define realloc xrealloc |
|
+#define free xfree |
|
+#endif |
|
+ |
|
+#define sa_table_alloc() (sa_table*)malloc(sizeof(sa_table)) |
|
+#define sa_table_xalloc() (sa_table*)calloc(1, sizeof(sa_table)) |
|
+#define sa_table_dealloc(table) free(table) |
|
+#define sa_entry_alloc(n) (sa_entry*)calloc((n), sizeof(sa_entry)) |
|
+#define sa_entry_dealloc(entries) free(entries) |
|
+ |
|
+#define SA_LAST 1 |
|
+#define SA_OFFSET 2 |
|
+ |
|
+#define SA_MIN_SIZE 4 |
|
+ |
|
+void |
|
+sa_init_table(register sa_table *table, sa_index_t num_bins) |
|
+{ |
|
+ if (num_bins) { |
|
+ table->num_entries = 0; |
|
+ table->entries = sa_entry_alloc(num_bins); |
|
+ table->num_bins = num_bins; |
|
+ table->free_pos = num_bins; |
|
+ } |
|
+ else { |
|
+ memset(table, 0, sizeof(sa_table)); |
|
+ } |
|
+} |
|
+ |
|
+sa_table* |
|
+sa_new_table() |
|
+{ |
|
+ sa_table* table = sa_table_alloc(); |
|
+ sa_init_table(table, 0); |
|
+ return table; |
|
+} |
|
+ |
|
+static inline sa_index_t |
|
+calc_pos(register sa_table* table, sa_index_t key) |
|
+{ |
|
+ /* this formula is empirical */ |
|
+ /* it has no good avalance, but works well in our case */ |
|
+ key ^= key >> 16; |
|
+ key *= 0x445229; |
|
+ return (key + (key >> 16)) % table->num_bins; |
|
+} |
|
+ |
|
+static void |
|
+fix_empty(register sa_table* table) |
|
+{ |
|
+ while(--table->free_pos && |
|
+ table->entries[table->free_pos-1].next != SA_EMPTY); |
|
+} |
|
+ |
|
+#define FLOOR_TO_4 ((~((sa_index_t)0)) << 2) |
|
+static sa_index_t |
|
+find_empty(register sa_table* table, register sa_index_t pos) |
|
+{ |
|
+ sa_index_t new_pos = table->free_pos-1; |
|
+ sa_entry *entry; |
|
+ pos &= FLOOR_TO_4; |
|
+ entry = table->entries+pos; |
|
+ |
|
+ if (entry->next == SA_EMPTY) { new_pos = pos; goto check; } |
|
+ pos++; entry++; |
|
+ if (entry->next == SA_EMPTY) { new_pos = pos; goto check; } |
|
+ pos++; entry++; |
|
+ if (entry->next == SA_EMPTY) { new_pos = pos; goto check; } |
|
+ pos++; entry++; |
|
+ if (entry->next == SA_EMPTY) { new_pos = pos; goto check; } |
|
+ |
|
+check: |
|
+ if (new_pos+1 == table->free_pos) fix_empty(table); |
|
+ return new_pos; |
|
+} |
|
+ |
|
+static void resize(register sa_table* table); |
|
+static int insert_into_chain(register sa_table*, register sa_index_t, st_data_t, sa_index_t pos); |
|
+static int insert_into_main(register sa_table*, sa_index_t, st_data_t, sa_index_t pos, sa_index_t prev_pos); |
|
+ |
|
+int |
|
+sa_insert(register sa_table* table, register sa_index_t key, st_data_t value) |
|
+{ |
|
+ sa_index_t pos, main_pos; |
|
+ register sa_entry *entry; |
|
+ |
|
+ if (table->num_bins == 0) { |
|
+ sa_init_table(table, SA_MIN_SIZE); |
|
+ } |
|
+ |
|
+ pos = calc_pos(table, key); |
|
+ entry = table->entries + pos; |
|
+ |
|
+ if (entry->next == SA_EMPTY) { |
|
+ entry->next = SA_LAST; |
|
+ entry->key = key; |
|
+ entry->value = value; |
|
+ table->num_entries++; |
|
+ if (pos+1 == table->free_pos) fix_empty(table); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ if (entry->key == key) { |
|
+ entry->value = value; |
|
+ return 1; |
|
+ } |
|
+ |
|
+ if (table->num_entries + (table->num_entries >> 2) > table->num_bins) { |
|
+ resize(table); |
|
+ return sa_insert(table, key, value); |
|
+ } |
|
+ |
|
+ main_pos = calc_pos(table, entry->key); |
|
+ if (main_pos == pos) { |
|
+ return insert_into_chain(table, key, value, pos); |
|
+ } |
|
+ else { |
|
+ if (!table->free_pos) { |
|
+ resize(table); |
|
+ return sa_insert(table, key, value); |
|
+ } |
|
+ return insert_into_main(table, key, value, pos, main_pos); |
|
+ } |
|
+} |
|
+ |
|
+static int |
|
+insert_into_chain(register sa_table* table, register sa_index_t key, st_data_t value, sa_index_t pos) |
|
+{ |
|
+ sa_entry *entry = table->entries + pos, *new_entry; |
|
+ sa_index_t new_pos; |
|
+ |
|
+ while (entry->next != SA_LAST) { |
|
+ pos = entry->next - SA_OFFSET; |
|
+ entry = table->entries + pos; |
|
+ if (entry->key == key) { |
|
+ entry->value = value; |
|
+ return 1; |
|
+ } |
|
+ } |
|
+ |
|
+ if (!table->free_pos) { |
|
+ resize(table); |
|
+ return sa_insert(table, key, value); |
|
+ } |
|
+ |
|
+ new_pos = find_empty(table, pos); |
|
+ new_entry = table->entries + new_pos; |
|
+ entry->next = new_pos + SA_OFFSET; |
|
+ |
|
+ new_entry->next = SA_LAST; |
|
+ new_entry->key = key; |
|
+ new_entry->value = value; |
|
+ table->num_entries++; |
|
+ return 0; |
|
+} |
|
+ |
|
+static int |
|
+insert_into_main(register sa_table* table, sa_index_t key, st_data_t value, sa_index_t pos, sa_index_t prev_pos) |
|
+{ |
|
+ sa_entry *entry = table->entries + pos; |
|
+ sa_index_t new_pos = find_empty(table, pos); |
|
+ sa_entry *new_entry = table->entries + new_pos; |
|
+ sa_index_t npos; |
|
+ |
|
+ *new_entry = *entry; |
|
+ |
|
+ while((npos = table->entries[prev_pos].next - SA_OFFSET) != pos) { |
|
+ prev_pos = npos; |
|
+ } |
|
+ table->entries[prev_pos].next = new_pos + SA_OFFSET; |
|
+ |
|
+ entry->next = SA_LAST; |
|
+ entry->key = key; |
|
+ entry->value = value; |
|
+ table->num_entries++; |
|
+ return 0; |
|
+} |
|
+ |
|
+static sa_index_t |
|
+new_size(sa_index_t num_entries) |
|
+{ |
|
+ sa_index_t msb = num_entries; |
|
+ msb |= msb >> 1; |
|
+ msb |= msb >> 2; |
|
+ msb |= msb >> 4; |
|
+ msb |= msb >> 8; |
|
+ msb |= msb >> 16; |
|
+ msb = ((msb >> 4) + 1) << 3; |
|
+ return (num_entries & (msb | (msb >> 1))) + (msb >> 1); |
|
+} |
|
+ |
|
+static void |
|
+resize(register sa_table *table) |
|
+{ |
|
+ sa_table tmp_table; |
|
+ sa_entry *entry; |
|
+ sa_index_t i; |
|
+ |
|
+ if (table->num_entries == 0) { |
|
+ sa_entry_dealloc(table->entries); |
|
+ memset(table, 0, sizeof(sa_table)); |
|
+ return; |
|
+ } |
|
+ |
|
+ sa_init_table(&tmp_table, new_size(table->num_entries + (table->num_entries >> 2))); |
|
+ entry = table->entries; |
|
+ |
|
+ for(i = 0; i < table->num_bins; i++, entry++) { |
|
+ if (entry->next != SA_EMPTY) { |
|
+ sa_insert(&tmp_table, entry->key, entry->value); |
|
+ } |
|
+ } |
|
+ sa_entry_dealloc(table->entries); |
|
+ *table = tmp_table; |
|
+} |
|
+ |
|
+int |
|
+sa_lookup(register sa_table *table, register sa_index_t key, st_data_t *value) |
|
+{ |
|
+ register sa_entry *entry; |
|
+ |
|
+ if (table->num_entries == 0) return 0; |
|
+ |
|
+ entry = table->entries + calc_pos(table, key); |
|
+ if (entry->next == SA_EMPTY) return 0; |
|
+ |
|
+ if (entry->key == key) goto found; |
|
+ if (entry->next == SA_LAST) return 0; |
|
+ |
|
+ entry = table->entries + (entry->next - SA_OFFSET); |
|
+ if (entry->key == key) goto found; |
|
+ |
|
+ while(entry->next != SA_LAST) { |
|
+ entry = table->entries + (entry->next - SA_OFFSET); |
|
+ if (entry->key == key) goto found; |
|
+ } |
|
+ return 0; |
|
+found: |
|
+ if (value) *value = entry->value; |
|
+ return 1; |
|
+} |
|
+ |
|
+void |
|
+sa_clear(sa_table *table) |
|
+{ |
|
+ sa_entry_dealloc(table->entries); |
|
+ memset(table, 0, sizeof(sa_table)); |
|
+} |
|
+ |
|
+void |
|
+sa_clear_no_free(sa_table *table) |
|
+{ |
|
+ memset(table->entries, 0, sizeof(sa_entry) * table->num_bins); |
|
+ table->num_entries = 0; |
|
+ table->free_pos = table->num_bins; |
|
+} |
|
+ |
|
+void |
|
+sa_free_table(sa_table *table) |
|
+{ |
|
+ sa_entry_dealloc(table->entries); |
|
+ sa_table_dealloc(table); |
|
+} |
|
+ |
|
+int |
|
+sa_delete(sa_table *table, sa_index_t key, st_data_t *value) |
|
+{ |
|
+ sa_index_t pos, prev_pos = ~0; |
|
+ sa_entry *entry; |
|
+ |
|
+ if (table->num_entries == 0) goto not_found; |
|
+ |
|
+ pos = calc_pos(table, key); |
|
+ entry = table->entries + pos; |
|
+ |
|
+ if (entry->next == SA_EMPTY) goto not_found; |
|
+ |
|
+ do { |
|
+ if (entry->key == key) { |
|
+ if (value) *value = entry->value; |
|
+ if (entry->next != SA_LAST) { |
|
+ sa_index_t npos = entry->next - SA_OFFSET; |
|
+ *entry = table->entries[npos]; |
|
+ memset(table->entries + npos, 0, sizeof(sa_entry)); |
|
+ } |
|
+ else { |
|
+ memset(table->entries + pos, 0, sizeof(sa_entry)); |
|
+ if (~prev_pos) { |
|
+ table->entries[prev_pos].next = SA_LAST; |
|
+ } |
|
+ } |
|
+ table->num_entries--; |
|
+ if (table->num_entries < table->num_bins / 4) { |
|
+ resize(table); |
|
+ } |
|
+ return 1; |
|
+ } |
|
+ if (entry->next == SA_LAST) break; |
|
+ prev_pos = pos; |
|
+ pos = entry->next - SA_OFFSET; |
|
+ entry = table->entries + pos; |
|
+ } while(1); |
|
+ |
|
+not_found: |
|
+ if (value) *value = 0; |
|
+ return 0; |
|
+} |
|
+ |
|
+int |
|
+sa_foreach(register sa_table *table, int (*func)(), st_data_t arg) |
|
+{ |
|
+ sa_index_t i; |
|
+ if (table->num_bins == 0) { |
|
+ return 0; |
|
+ } |
|
+ for(i = 0; i < table->num_bins ; i++) { |
|
+ if (table->entries[i].next != SA_EMPTY) { |
|
+ sa_index_t key = table->entries[i].key; |
|
+ st_data_t val = table->entries[i].value; |
|
+ if ((*func)(key, val, arg) == SA_STOP) break; |
|
+ } |
|
+ } |
|
+ return 0; |
|
+} |
|
+ |
|
+size_t |
|
+sa_memsize(const sa_table *table) |
|
+{ |
|
+ return sizeof(sa_table) + table->num_bins * sizeof(sa_entry); |
|
+} |
|
+ |
|
+sa_table* |
|
+sa_copy(sa_table *table) |
|
+{ |
|
+ sa_table *new_table = sa_table_alloc(); |
|
+ *new_table = *table; |
|
+ if (table->num_bins) { |
|
+ new_table->entries = sa_entry_alloc(table->num_bins); |
|
+ memcpy(new_table->entries, table->entries, table->num_bins*sizeof(sa_entry)); |
|
+ } |
|
+ return new_table; |
|
+} |
|
+ |
|
+void |
|
+sa_copy_to(sa_table *from, sa_table *to) |
|
+{ |
|
+ sa_entry_dealloc(to->entries); |
|
+ *to = *from; |
|
+ if (to->num_bins) { |
|
+ to->entries = sa_entry_alloc(to->num_bins); |
|
+ memcpy(to->entries, from->entries, from->num_bins*sizeof(sa_entry)); |
|
+ } |
|
+} |
|
diff --git a/st.c b/st.c |
|
index fda5784..20ec427 100644 |
|
--- a/st.c |
|
+++ b/st.c |
|
@@ -7,6 +7,7 @@ |
|
#include "st.h" |
|
#else |
|
#include "ruby/ruby.h" |
|
+#include "pool_alloc.h" |
|
#endif |
|
|
|
#include <stdio.h> |
|
@@ -25,8 +26,17 @@ struct st_table_entry { |
|
st_table_entry *fore, *back; |
|
}; |
|
|
|
-#define ST_DEFAULT_MAX_DENSITY 5 |
|
+#define STATIC_ASSERT(name, expr) typedef int static_assert_##name##_check[(expr) ? 1 : -1]; |
|
+ |
|
+#define ST_DEFAULT_MAX_DENSITY 2 |
|
#define ST_DEFAULT_INIT_TABLE_SIZE 11 |
|
+#define ST_DEFAULT_SECOND_TABLE_SIZE 19 |
|
+#define ST_DEFAULT_PACKED_TABLE_SIZE 18 |
|
+#define PACKED_UNIT (int)(sizeof(st_packed_entry) / sizeof(st_table_entry*)) |
|
+#define MAX_PACKED_HASH (int)(ST_DEFAULT_PACKED_TABLE_SIZE * sizeof(st_table_entry*) / sizeof(st_packed_entry)) |
|
+ |
|
+STATIC_ASSERT(st_packed_entry, sizeof(st_packed_entry) == sizeof(st_table_entry*[PACKED_UNIT])) |
|
+STATIC_ASSERT(st_packed_bins, sizeof(st_packed_entry[MAX_PACKED_HASH]) <= sizeof(st_table_entry*[ST_DEFAULT_PACKED_TABLE_SIZE])) |
|
|
|
/* |
|
* DEFAULT_MAX_DENSITY is the default for the largest we allow the |
|
@@ -38,7 +48,8 @@ struct st_table_entry { |
|
* |
|
*/ |
|
|
|
-static const struct st_hash_type type_numhash = { |
|
+#define type_numhash st_hashtype_num |
|
+const struct st_hash_type st_hashtype_num = { |
|
st_numcmp, |
|
st_numhash, |
|
}; |
|
@@ -61,20 +72,128 @@ static void rehash(st_table *); |
|
#ifdef RUBY |
|
#define malloc xmalloc |
|
#define calloc xcalloc |
|
+#define realloc xrealloc |
|
#define free(x) xfree(x) |
|
#endif |
|
|
|
#define numberof(array) (int)(sizeof(array) / sizeof((array)[0])) |
|
|
|
-#define alloc(type) (type*)malloc((size_t)sizeof(type)) |
|
-#define Calloc(n,s) (char*)calloc((n),(s)) |
|
- |
|
#define EQUAL(table,x,y) ((x)==(y) || (*(table)->type->compare)((x),(y)) == 0) |
|
|
|
-/* remove cast to unsigned int in the future */ |
|
-#define do_hash(key,table) (unsigned int)(st_index_t)(*(table)->type->hash)((key)) |
|
+#define do_hash(key,table) (st_index_t)(*(table)->type->hash)((key)) |
|
#define do_hash_bin(key,table) (do_hash((key), (table))%(table)->num_bins) |
|
|
|
+/* preparation for possible allocation improvements */ |
|
+#ifdef POOL_ALLOC_API |
|
+#define st_alloc_entry() (st_table_entry *)ruby_xpool_malloc_6p() |
|
+#define st_free_entry(entry) ruby_xpool_free(entry) |
|
+#define st_alloc_table() (st_table *)ruby_xpool_malloc_6p() |
|
+#define st_dealloc_table(table) ruby_xpool_free(table) |
|
+static inline st_table_entry ** |
|
+st_alloc_bins(st_index_t size) |
|
+{ |
|
+ st_table_entry **result; |
|
+ if (size == 11) { |
|
+ result = (st_table_entry **) ruby_xpool_malloc_11p(); |
|
+ memset(result, 0, 11 * sizeof(st_table_entry *)); |
|
+ } |
|
+ else |
|
+ result = (st_table_entry **) ruby_xcalloc(size, sizeof(st_table_entry*)); |
|
+ return result; |
|
+} |
|
+static inline void |
|
+st_free_bins(st_table_entry **bins, st_index_t size) |
|
+{ |
|
+ if (size == 11) |
|
+ ruby_xpool_free(bins); |
|
+ else |
|
+ ruby_xfree(bins); |
|
+} |
|
+static inline st_table_entry** |
|
+st_realloc_bins(st_table_entry **bins, st_index_t newsize, st_index_t oldsize) |
|
+{ |
|
+ st_table_entry **new_bins = st_alloc_bins(newsize); |
|
+ st_free_bins(bins, oldsize); |
|
+ return new_bins; |
|
+} |
|
+#else |
|
+#define st_alloc_entry() (st_table_entry *)malloc(sizeof(st_table_entry)) |
|
+#define st_free_entry(entry) free(entry) |
|
+#define st_alloc_table() (st_table *)malloc(sizeof(st_table)) |
|
+#define st_dealloc_table(table) free(table) |
|
+#define st_alloc_bins(size) (st_table_entry **)calloc(size, sizeof(st_table_entry *)) |
|
+#define st_free_bins(bins, size) free(bins) |
|
+static inline st_table_entry** |
|
+st_realloc_bins(st_table_entry **bins, st_index_t newsize, st_index_t oldsize) |
|
+{ |
|
+ bins = (st_table_entry **)realloc(bins, newsize * sizeof(st_table_entry *)); |
|
+ MEMZERO(bins, st_table_entry*, newsize); |
|
+ return bins; |
|
+} |
|
+#endif |
|
+ |
|
+/* Shortage */ |
|
+#define bins as.big.bins |
|
+#define head as.big.head |
|
+#define tail as.big.tail |
|
+#define real_entries as.packed.real_entries |
|
+ |
|
+/* preparation for possible packing improvements */ |
|
+#define PACKED_BINS(table) ((table)->as.packed.entries) |
|
+#define PACKED_ENT(table, i) PACKED_BINS(table)[i] |
|
+#define PKEY(table, i) PACKED_ENT((table), (i)).key |
|
+#define PVAL(table, i) PACKED_ENT((table), (i)).val |
|
+#define PHASH(table, i) PACKED_ENT((table), (i)).hash |
|
+#define PKEY_SET(table, i, v) (PKEY((table), (i)) = (v)) |
|
+#define PVAL_SET(table, i, v) (PVAL((table), (i)) = (v)) |
|
+#define PHASH_SET(table, i, v) (PHASH((table), (i)) = (v)) |
|
+ |
|
+/* this function depends much on packed layout, so that it placed here */ |
|
+static inline void |
|
+remove_packed_entry(st_table *table, st_index_t i) |
|
+{ |
|
+ table->real_entries--; |
|
+ table->num_entries--; |
|
+ if (i < table->real_entries) { |
|
+ MEMMOVE(&PACKED_ENT(table, i), &PACKED_ENT(table, i+1), |
|
+ st_packed_entry, table->real_entries - i); |
|
+ } |
|
+} |
|
+ |
|
+static inline void |
|
+remove_safe_packed_entry(st_table *table, st_index_t i, st_data_t never) |
|
+{ |
|
+ table->num_entries--; |
|
+ PKEY_SET(table, i, never); |
|
+ PVAL_SET(table, i, never); |
|
+ PHASH_SET(table, i, 0); |
|
+} |
|
+ |
|
+/* ultrapacking */ |
|
+#define real_upacked num_bins |
|
+#define MAX_UPACKED_HASH 1 |
|
+#define ULTRAPACKED(table) ((table)->real_upacked <= 1) |
|
+#define UPACKED_ENT(table) ((table)->as.upacked) |
|
+#define UPKEY(table) UPACKED_ENT(table).key |
|
+#define UPVAL(table) UPACKED_ENT(table).val |
|
+#define UPHASH(table) UPACKED_ENT(table).hash |
|
+#define UPKEY_SET(table, v) (UPACKED_ENT(table).key = (v)) |
|
+#define UPVAL_SET(table, v) (UPACKED_ENT(table).val = (v)) |
|
+#define UPHASH_SET(table, v) (UPACKED_ENT(table).hash = (v)) |
|
+static inline void |
|
+remove_upacked_entry(st_table *table) |
|
+{ |
|
+ table->real_upacked = table->num_entries = 0; |
|
+} |
|
+ |
|
+static inline void |
|
+remove_safe_upacked_entry(st_table *table, st_data_t never) |
|
+{ |
|
+ table->num_entries = 0; |
|
+ UPKEY_SET(table, never); |
|
+ UPVAL_SET(table, never); |
|
+ UPHASH_SET(table, 0); |
|
+} |
|
/* |
|
* MINSIZE is the minimum size of a dictionary. |
|
*/ |
|
@@ -85,8 +204,8 @@ static void rehash(st_table *); |
|
Table of prime numbers 2^n+a, 2<=n<=30. |
|
*/ |
|
static const unsigned int primes[] = { |
|
- 8 + 3, |
|
- 16 + 3, |
|
+ ST_DEFAULT_INIT_TABLE_SIZE, |
|
+ ST_DEFAULT_SECOND_TABLE_SIZE, |
|
32 + 5, |
|
64 + 3, |
|
128 + 3, |
|
@@ -161,8 +280,6 @@ stat_col(void) |
|
} |
|
#endif |
|
|
|
-#define MAX_PACKED_NUMHASH (ST_DEFAULT_INIT_TABLE_SIZE/2) |
|
- |
|
st_table* |
|
st_init_table_with_size(const struct st_hash_type *type, st_index_t size) |
|
{ |
|
@@ -181,14 +298,19 @@ st_init_table_with_size(const struct st_hash_type *type, st_index_t size) |
|
} |
|
#endif |
|
|
|
- size = new_size(size); /* round up to prime number */ |
|
|
|
- tbl = alloc(st_table); |
|
+ tbl = st_alloc_table(); |
|
tbl->type = type; |
|
tbl->num_entries = 0; |
|
- tbl->entries_packed = type == &type_numhash && size/2 <= MAX_PACKED_NUMHASH; |
|
+ tbl->entries_packed = size <= MAX_PACKED_HASH; |
|
+ if (tbl->entries_packed) { |
|
+ size = size <= MAX_UPACKED_HASH ? 0 : ST_DEFAULT_PACKED_TABLE_SIZE; |
|
+ } |
|
+ else { |
|
+ size = new_size(size); /* round up to prime number */ |
|
+ } |
|
tbl->num_bins = size; |
|
- tbl->bins = (st_table_entry **)Calloc(size, sizeof(st_table_entry*)); |
|
+ tbl->bins = size ? st_alloc_bins(size) : 0; |
|
tbl->head = 0; |
|
tbl->tail = 0; |
|
|
|
@@ -243,17 +365,23 @@ st_clear(st_table *table) |
|
register st_table_entry *ptr, *next; |
|
st_index_t i; |
|
|
|
+ if (ULTRAPACKED(table)) { |
|
+ remove_upacked_entry(table); |
|
+ return; |
|
+ } |
|
+ |
|
if (table->entries_packed) { |
|
table->num_entries = 0; |
|
+ table->real_entries = 0; |
|
return; |
|
} |
|
|
|
- for(i = 0; i < table->num_bins; i++) { |
|
+ for (i = 0; i < table->num_bins; i++) { |
|
ptr = table->bins[i]; |
|
table->bins[i] = 0; |
|
while (ptr != 0) { |
|
next = ptr->next; |
|
- free(ptr); |
|
+ st_free_entry(ptr); |
|
ptr = next; |
|
} |
|
} |
|
@@ -266,14 +394,19 @@ void |
|
st_free_table(st_table *table) |
|
{ |
|
st_clear(table); |
|
- free(table->bins); |
|
- free(table); |
|
+ if (!ULTRAPACKED(table)) { |
|
+ st_free_bins(table->bins, table->num_bins); |
|
+ } |
|
+ st_dealloc_table(table); |
|
} |
|
|
|
size_t |
|
st_memsize(const st_table *table) |
|
{ |
|
- if (table->entries_packed) { |
|
+ if (ULTRAPACKED(table)) { |
|
+ return sizeof(st_table); |
|
+ } |
|
+ else if (table->entries_packed) { |
|
return table->num_bins * sizeof (void *) + sizeof(st_table); |
|
} |
|
else { |
|
@@ -306,46 +439,77 @@ count_collision(const struct st_hash_type *type) |
|
#define FOUND_ENTRY |
|
#endif |
|
|
|
-#define FIND_ENTRY(table, ptr, hash_val, bin_pos) do {\ |
|
- (bin_pos) = (hash_val)%(table)->num_bins;\ |
|
- (ptr) = (table)->bins[(bin_pos)];\ |
|
- FOUND_ENTRY;\ |
|
- if (PTR_NOT_EQUAL((table), (ptr), (hash_val), key)) {\ |
|
- COLLISION;\ |
|
- while (PTR_NOT_EQUAL((table), (ptr)->next, (hash_val), key)) {\ |
|
- (ptr) = (ptr)->next;\ |
|
- }\ |
|
- (ptr) = (ptr)->next;\ |
|
- }\ |
|
-} while (0) |
|
+#define FIND_ENTRY(table, ptr, hash_val, bin_pos) \ |
|
+ ((ptr) = find_entry((table), key, (hash_val), ((bin_pos) = (hash_val)%(table)->num_bins))) |
|
+ |
|
+static st_table_entry * |
|
+find_entry(st_table *table, st_data_t key, st_index_t hash_val, st_index_t bin_pos) |
|
+{ |
|
+ register st_table_entry *ptr = table->bins[bin_pos]; |
|
+ FOUND_ENTRY; |
|
+ if (PTR_NOT_EQUAL(table, ptr, hash_val, key)) { |
|
+ COLLISION; |
|
+ while (PTR_NOT_EQUAL(table, ptr->next, hash_val, key)) { |
|
+ ptr = ptr->next; |
|
+ } |
|
+ ptr = ptr->next; |
|
+ } |
|
+ return ptr; |
|
+} |
|
+ |
|
+static inline st_index_t |
|
+find_packed_index(st_table *table, st_index_t hash_val, st_data_t key) |
|
+{ |
|
+ st_index_t i = 0; |
|
+ while (i < table->real_entries && |
|
+ (PHASH(table, i) != hash_val || !EQUAL(table, key, PKEY(table, i)))) { |
|
+ i++; |
|
+ } |
|
+ return i; |
|
+} |
|
+ |
|
+static inline int |
|
+check_upacked(st_table *table, st_index_t hash_val, st_data_t key) |
|
+{ |
|
+ return table->num_entries && |
|
+ UPHASH(table) == hash_val && |
|
+ EQUAL(table, key, UPKEY(table)); |
|
+} |
|
|
|
#define collision_check 0 |
|
|
|
int |
|
st_lookup(st_table *table, register st_data_t key, st_data_t *value) |
|
{ |
|
- st_index_t hash_val, bin_pos; |
|
+ st_index_t hash_val; |
|
register st_table_entry *ptr; |
|
|
|
+ hash_val = do_hash(key, table); |
|
+ |
|
+ if (ULTRAPACKED(table)) { |
|
+ if (check_upacked(table, hash_val, key)) { |
|
+ if (value != 0) *value = UPVAL(table); |
|
+ return 1; |
|
+ } |
|
+ return 0; |
|
+ } |
|
+ |
|
if (table->entries_packed) { |
|
- st_index_t i; |
|
- for (i = 0; i < table->num_entries; i++) { |
|
- if ((st_data_t)table->bins[i*2] == key) { |
|
- if (value !=0) *value = (st_data_t)table->bins[i*2+1]; |
|
- return 1; |
|
- } |
|
- } |
|
+ st_index_t i = find_packed_index(table, hash_val, key); |
|
+ if (i < table->real_entries) { |
|
+ if (value != 0) *value = PVAL(table, i); |
|
+ return 1; |
|
+ } |
|
return 0; |
|
} |
|
|
|
- hash_val = do_hash(key, table); |
|
- FIND_ENTRY(table, ptr, hash_val, bin_pos); |
|
+ ptr = find_entry(table, key, hash_val, hash_val % table->num_bins); |
|
|
|
if (ptr == 0) { |
|
return 0; |
|
} |
|
else { |
|
- if (value != 0) *value = ptr->record; |
|
+ if (value != 0) *value = ptr->record; |
|
return 1; |
|
} |
|
} |
|
@@ -353,22 +517,29 @@ st_lookup(st_table *table, register st_data_t key, st_data_t *value) |
|
int |
|
st_get_key(st_table *table, register st_data_t key, st_data_t *result) |
|
{ |
|
- st_index_t hash_val, bin_pos; |
|
+ st_index_t hash_val; |
|
register st_table_entry *ptr; |
|
|
|
+ hash_val = do_hash(key, table); |
|
+ |
|
+ if (ULTRAPACKED(table)) { |
|
+ if (check_upacked(table, hash_val, key)) { |
|
+ if (result != 0) *result = UPKEY(table); |
|
+ return 1; |
|
+ } |
|
+ return 0; |
|
+ } |
|
+ |
|
if (table->entries_packed) { |
|
- st_index_t i; |
|
- for (i = 0; i < table->num_entries; i++) { |
|
- if ((st_data_t)table->bins[i*2] == key) { |
|
- if (result !=0) *result = (st_data_t)table->bins[i*2]; |
|
- return 1; |
|
- } |
|
- } |
|
+ st_index_t i = find_packed_index(table, hash_val, key); |
|
+ if (i < table->real_entries) { |
|
+ if (result != 0) *result = PKEY(table, i); |
|
+ return 1; |
|
+ } |
|
return 0; |
|
} |
|
|
|
- hash_val = do_hash(key, table); |
|
- FIND_ENTRY(table, ptr, hash_val, bin_pos); |
|
+ ptr = find_entry(table, key, hash_val, hash_val % table->num_bins); |
|
|
|
if (ptr == 0) { |
|
return 0; |
|
@@ -382,85 +553,151 @@ st_get_key(st_table *table, register st_data_t key, st_data_t *result) |
|
#undef collision_check |
|
#define collision_check 1 |
|
|
|
-#define MORE_PACKABLE_P(table) \ |
|
- ((st_index_t)((table)->num_entries+1) * 2 <= (table)->num_bins && \ |
|
- (table)->num_entries+1 <= MAX_PACKED_NUMHASH) |
|
- |
|
-#define ADD_DIRECT(table, key, value, hash_val, bin_pos)\ |
|
-do {\ |
|
- st_table_entry *entry;\ |
|
- if ((table)->num_entries > ST_DEFAULT_MAX_DENSITY * (table)->num_bins) {\ |
|
- rehash(table);\ |
|
- (bin_pos) = (hash_val) % (table)->num_bins;\ |
|
- }\ |
|
- \ |
|
- entry = alloc(st_table_entry);\ |
|
- \ |
|
- entry->hash = (hash_val);\ |
|
- entry->key = (key);\ |
|
- entry->record = (value);\ |
|
- entry->next = (table)->bins[(bin_pos)];\ |
|
- if ((table)->head != 0) {\ |
|
- entry->fore = 0;\ |
|
- (entry->back = (table)->tail)->fore = entry;\ |
|
- (table)->tail = entry;\ |
|
- }\ |
|
- else {\ |
|
- (table)->head = (table)->tail = entry;\ |
|
- entry->fore = entry->back = 0;\ |
|
- }\ |
|
- (table)->bins[(bin_pos)] = entry;\ |
|
- (table)->num_entries++;\ |
|
-} while (0) |
|
+static inline st_table_entry * |
|
+new_entry(st_table * table, st_data_t key, st_data_t value, |
|
+ st_index_t hash_val, register st_index_t bin_pos) |
|
+{ |
|
+ register st_table_entry *entry = st_alloc_entry(); |
|
+ |
|
+ entry->next = table->bins[bin_pos]; |
|
+ table->bins[bin_pos] = entry; |
|
+ entry->hash = hash_val; |
|
+ entry->key = key; |
|
+ entry->record = value; |
|
+ |
|
+ return entry; |
|
+} |
|
+ |
|
+static inline void |
|
+add_direct(st_table *table, st_data_t key, st_data_t value, |
|
+ st_index_t hash_val, register st_index_t bin_pos) |
|
+{ |
|
+ register st_table_entry *entry; |
|
+ if (table->num_entries > ST_DEFAULT_MAX_DENSITY * table->num_bins) { |
|
+ rehash(table); |
|
+ bin_pos = hash_val % table->num_bins; |
|
+ } |
|
+ |
|
+ entry = new_entry(table, key, value, hash_val, bin_pos); |
|
+ |
|
+ if (table->head != 0) { |
|
+ entry->fore = 0; |
|
+ (entry->back = table->tail)->fore = entry; |
|
+ table->tail = entry; |
|
+ } |
|
+ else { |
|
+ table->head = table->tail = entry; |
|
+ entry->fore = entry->back = 0; |
|
+ } |
|
+ table->num_entries++; |
|
+} |
|
|
|
static void |
|
unpack_entries(register st_table *table) |
|
{ |
|
st_index_t i; |
|
- struct st_table_entry *packed_bins[MAX_PACKED_NUMHASH*2]; |
|
+ st_packed_entry packed_bins[MAX_PACKED_HASH]; |
|
+ register st_table_entry *entry, *preventry = 0, **chain; |
|
st_table tmp_table = *table; |
|
|
|
- memcpy(packed_bins, table->bins, sizeof(struct st_table_entry *) * table->num_entries*2); |
|
- table->bins = packed_bins; |
|
+ MEMCPY(packed_bins, PACKED_BINS(table), st_packed_entry, MAX_PACKED_HASH); |
|
+ table->as.packed.entries = packed_bins; |
|
tmp_table.entries_packed = 0; |
|
- tmp_table.num_entries = 0; |
|
- memset(tmp_table.bins, 0, sizeof(struct st_table_entry *) * tmp_table.num_bins); |
|
- for (i = 0; i < table->num_entries; i++) { |
|
- st_insert(&tmp_table, (st_data_t)packed_bins[i*2], (st_data_t)packed_bins[i*2+1]); |
|
- } |
|
+#if ST_DEFAULT_INIT_TABLE_SIZE == ST_DEFAULT_PACKED_TABLE_SIZE |
|
+ MEMZERO(tmp_table.bins, st_table_entry*, tmp_table.num_bins); |
|
+#else |
|
+ tmp_table.bins = st_realloc_bins(tmp_table.bins, ST_DEFAULT_INIT_TABLE_SIZE, tmp_table.num_bins); |
|
+ tmp_table.num_bins = ST_DEFAULT_INIT_TABLE_SIZE; |
|
+#endif |
|
+ i = 0; |
|
+ chain = &tmp_table.head; |
|
+ do { |
|
+ st_data_t key = packed_bins[i].key; |
|
+ st_data_t val = packed_bins[i].val; |
|
+ st_index_t hash = packed_bins[i].hash; |
|
+ entry = new_entry(&tmp_table, key, val, hash, |
|
+ hash % ST_DEFAULT_INIT_TABLE_SIZE); |
|
+ *chain = entry; |
|
+ entry->back = preventry; |
|
+ preventry = entry; |
|
+ chain = &entry->fore; |
|
+ } while (++i < MAX_PACKED_HASH); |
|
+ *chain = NULL; |
|
+ tmp_table.tail = entry; |
|
*table = tmp_table; |
|
} |
|
|
|
+static void |
|
+add_packed_direct(st_table *table, st_data_t key, st_data_t value, st_index_t hash_val) |
|
+{ |
|
+ if (table->real_entries < MAX_PACKED_HASH) { |
|
+ st_index_t i = table->real_entries++; |
|
+ PKEY_SET(table, i, key); |
|
+ PVAL_SET(table, i, value); |
|
+ PHASH_SET(table, i, hash_val); |
|
+ table->num_entries++; |
|
+ } |
|
+ else { |
|
+ unpack_entries(table); |
|
+ add_direct(table, key, value, hash_val, hash_val % table->num_bins); |
|
+ } |
|
+} |
|
+ |
|
+static void |
|
+add_upacked_direct(register st_table *table, register st_data_t key, st_data_t value, st_index_t hash_val) |
|
+{ |
|
+ if (table->real_upacked) { |
|
+ st_packed_entry *entries = (st_packed_entry *) st_alloc_bins(ST_DEFAULT_PACKED_TABLE_SIZE); |
|
+ entries[0] = UPACKED_ENT(table); |
|
+ entries[1].hash = hash_val; |
|
+ entries[1].key = key; |
|
+ entries[1].val = value; |
|
+ table->num_bins = ST_DEFAULT_PACKED_TABLE_SIZE; |
|
+ table->real_entries = 2; |
|
+ table->num_entries++; |
|
+ table->as.packed.entries = entries; |
|
+ } |
|
+ else { |
|
+ table->real_upacked = 1; |
|
+ table->num_entries = 1; |
|
+ UPHASH_SET(table, hash_val); |
|
+ UPKEY_SET(table, key); |
|
+ UPVAL_SET(table, value); |
|
+ } |
|
+} |
|
+ |
|
int |
|
st_insert(register st_table *table, register st_data_t key, st_data_t value) |
|
{ |
|
- st_index_t hash_val, bin_pos; |
|
+ st_index_t hash_val; |
|
+ register st_index_t bin_pos; |
|
register st_table_entry *ptr; |
|
|
|
+ hash_val = do_hash(key, table); |
|
+ |
|
+ if (ULTRAPACKED(table)) { |
|
+ if (check_upacked(table, hash_val, key)) { |
|
+ UPVAL_SET(table, value); |
|
+ return 1; |
|
+ } |
|
+ add_upacked_direct(table, key, value, hash_val); |
|
+ return 0; |
|
+ } |
|
+ |
|
if (table->entries_packed) { |
|
- st_index_t i; |
|
- for (i = 0; i < table->num_entries; i++) { |
|
- if ((st_data_t)table->bins[i*2] == key) { |
|
- table->bins[i*2+1] = (struct st_table_entry*)value; |
|
- return 1; |
|
- } |
|
- } |
|
- if (MORE_PACKABLE_P(table)) { |
|
- i = table->num_entries++; |
|
- table->bins[i*2] = (struct st_table_entry*)key; |
|
- table->bins[i*2+1] = (struct st_table_entry*)value; |
|
- return 0; |
|
- } |
|
- else { |
|
- unpack_entries(table); |
|
+ st_index_t i = find_packed_index(table, hash_val, key); |
|
+ if (i < table->real_entries) { |
|
+ PVAL_SET(table, i, value); |
|
+ return 1; |
|
} |
|
+ add_packed_direct(table, key, value, hash_val); |
|
+ return 0; |
|
} |
|
|
|
- hash_val = do_hash(key, table); |
|
FIND_ENTRY(table, ptr, hash_val, bin_pos); |
|
|
|
if (ptr == 0) { |
|
- ADD_DIRECT(table, key, value, hash_val, bin_pos); |
|
+ add_direct(table, key, value, hash_val, bin_pos); |
|
return 0; |
|
} |
|
else { |
|
@@ -473,34 +710,38 @@ int |
|
st_insert2(register st_table *table, register st_data_t key, st_data_t value, |
|
st_data_t (*func)(st_data_t)) |
|
{ |
|
- st_index_t hash_val, bin_pos; |
|
+ st_index_t hash_val; |
|
+ register st_index_t bin_pos; |
|
register st_table_entry *ptr; |
|
|
|
+ hash_val = do_hash(key, table); |
|
+ |
|
+ if (ULTRAPACKED(table)) { |
|
+ if (check_upacked(table, hash_val, key)) { |
|
+ UPVAL_SET(table, value); |
|
+ return 1; |
|
+ } |
|
+ key = (*func)(key); |
|
+ add_upacked_direct(table, key, value, hash_val); |
|
+ return 0; |
|
+ } |
|
+ |
|
if (table->entries_packed) { |
|
- st_index_t i; |
|
- for (i = 0; i < table->num_entries; i++) { |
|
- if ((st_data_t)table->bins[i*2] == key) { |
|
- table->bins[i*2+1] = (struct st_table_entry*)value; |
|
- return 1; |
|
- } |
|
- } |
|
- if (MORE_PACKABLE_P(table)) { |
|
- i = table->num_entries++; |
|
- table->bins[i*2] = (struct st_table_entry*)key; |
|
- table->bins[i*2+1] = (struct st_table_entry*)value; |
|
- return 0; |
|
- } |
|
- else { |
|
- unpack_entries(table); |
|
- } |
|
+ st_index_t i = find_packed_index(table, hash_val, key); |
|
+ if (i < table->real_entries) { |
|
+ PVAL_SET(table, i, value); |
|
+ return 1; |
|
+ } |
|
+ key = (*func)(key); |
|
+ add_packed_direct(table, key, value, hash_val); |
|
+ return 0; |
|
} |
|
|
|
- hash_val = do_hash(key, table); |
|
FIND_ENTRY(table, ptr, hash_val, bin_pos); |
|
|
|
if (ptr == 0) { |
|
key = (*func)(key); |
|
- ADD_DIRECT(table, key, value, hash_val, bin_pos); |
|
+ add_direct(table, key, value, hash_val, bin_pos); |
|
return 0; |
|
} |
|
else { |
|
@@ -512,36 +753,30 @@ st_insert2(register st_table *table, register st_data_t key, st_data_t value, |
|
void |
|
st_add_direct(st_table *table, st_data_t key, st_data_t value) |
|
{ |
|
- st_index_t hash_val, bin_pos; |
|
+ st_index_t hash_val; |
|
+ |
|
+ hash_val = do_hash(key, table); |
|
+ if (ULTRAPACKED(table)) { |
|
+ add_upacked_direct(table, key, value, hash_val); |
|
+ return; |
|
+ } |
|
|
|
if (table->entries_packed) { |
|
- int i; |
|
- if (MORE_PACKABLE_P(table)) { |
|
- i = table->num_entries++; |
|
- table->bins[i*2] = (struct st_table_entry*)key; |
|
- table->bins[i*2+1] = (struct st_table_entry*)value; |
|
- return; |
|
- } |
|
- else { |
|
- unpack_entries(table); |
|
- } |
|
+ add_packed_direct(table, key, value, hash_val); |
|
+ return; |
|
} |
|
|
|
- hash_val = do_hash(key, table); |
|
- bin_pos = hash_val % table->num_bins; |
|
- ADD_DIRECT(table, key, value, hash_val, bin_pos); |
|
+ add_direct(table, key, value, hash_val, hash_val % table->num_bins); |
|
} |
|
|
|
static void |
|
rehash(register st_table *table) |
|
{ |
|
register st_table_entry *ptr, **new_bins; |
|
- st_index_t i, new_num_bins, hash_val; |
|
+ st_index_t new_num_bins, hash_val; |
|
|
|
new_num_bins = new_size(table->num_bins+1); |
|
- new_bins = (st_table_entry**) |
|
- xrealloc(table->bins, new_num_bins * sizeof(st_table_entry*)); |
|
- for (i = 0; i < new_num_bins; ++i) new_bins[i] = 0; |
|
+ new_bins = st_realloc_bins(table->bins, new_num_bins, table->num_bins); |
|
table->num_bins = new_num_bins; |
|
table->bins = new_bins; |
|
|
|
@@ -558,34 +793,37 @@ st_table* |
|
st_copy(st_table *old_table) |
|
{ |
|
st_table *new_table; |
|
- st_table_entry *ptr, *entry, *prev, **tail; |
|
+ st_table_entry *ptr, *entry, *prev, **tailp; |
|
st_index_t num_bins = old_table->num_bins; |
|
st_index_t hash_val; |
|
|
|
- new_table = alloc(st_table); |
|
+ new_table = st_alloc_table(); |
|
if (new_table == 0) { |
|
return 0; |
|
} |
|
|
|
*new_table = *old_table; |
|
- new_table->bins = (st_table_entry**) |
|
- Calloc((unsigned)num_bins, sizeof(st_table_entry*)); |
|
+ if (ULTRAPACKED(old_table)) { |
|
+ return new_table; |
|
+ } |
|
+ |
|
+ new_table->bins = st_alloc_bins(num_bins); |
|
|
|
if (new_table->bins == 0) { |
|
- free(new_table); |
|
+ st_dealloc_table(new_table); |
|
return 0; |
|
} |
|
|
|
if (old_table->entries_packed) { |
|
- memcpy(new_table->bins, old_table->bins, sizeof(struct st_table_entry *) * old_table->num_bins); |
|
+ MEMCPY(new_table->bins, old_table->bins, st_table_entry*, old_table->num_bins); |
|
return new_table; |
|
} |
|
|
|
if ((ptr = old_table->head) != 0) { |
|
prev = 0; |
|
- tail = &new_table->head; |
|
+ tailp = &new_table->head; |
|
do { |
|
- entry = alloc(st_table_entry); |
|
+ entry = st_alloc_entry(); |
|
if (entry == 0) { |
|
st_free_table(new_table); |
|
return 0; |
|
@@ -595,8 +833,8 @@ st_copy(st_table *old_table) |
|
entry->next = new_table->bins[hash_val]; |
|
new_table->bins[hash_val] = entry; |
|
entry->back = prev; |
|
- *tail = prev = entry; |
|
- tail = &entry->fore; |
|
+ *tailp = prev = entry; |
|
+ tailp = &entry->fore; |
|
} while ((ptr = ptr->fore) != 0); |
|
new_table->tail = prev; |
|
} |
|
@@ -604,21 +842,22 @@ st_copy(st_table *old_table) |
|
return new_table; |
|
} |
|
|
|
-#define REMOVE_ENTRY(table, ptr) do \ |
|
- { \ |
|
- if ((ptr)->fore == 0 && (ptr)->back == 0) { \ |
|
- (table)->head = 0; \ |
|
- (table)->tail = 0; \ |
|
- } \ |
|
- else { \ |
|
- st_table_entry *fore = (ptr)->fore, *back = (ptr)->back; \ |
|
- if (fore) fore->back = back; \ |
|
- if (back) back->fore = fore; \ |
|
- if ((ptr) == (table)->head) (table)->head = fore; \ |
|
- if ((ptr) == (table)->tail) (table)->tail = back; \ |
|
- } \ |
|
- (table)->num_entries--; \ |
|
- } while (0) |
|
+static inline void |
|
+remove_entry(st_table *table, st_table_entry *ptr) |
|
+{ |
|
+ if (ptr->fore == 0 && ptr->back == 0) { |
|
+ table->head = 0; |
|
+ table->tail = 0; |
|
+ } |
|
+ else { |
|
+ st_table_entry *fore = ptr->fore, *back = ptr->back; |
|
+ if (fore) fore->back = back; |
|
+ if (back) back->fore = fore; |
|
+ if (ptr == table->head) table->head = fore; |
|
+ if (ptr == table->tail) table->tail = back; |
|
+ } |
|
+ table->num_entries--; |
|
+} |
|
|
|
int |
|
st_delete(register st_table *table, register st_data_t *key, st_data_t *value) |
|
@@ -627,30 +866,38 @@ st_delete(register st_table *table, register st_data_t *key, st_data_t *value) |
|
st_table_entry **prev; |
|
register st_table_entry *ptr; |
|
|
|
+ hash_val = do_hash(*key, table); |
|
+ |
|
+ if (ULTRAPACKED(table)) { |
|
+ if (check_upacked(table, hash_val, *key)) { |
|
+ if (value != 0) *value = UPVAL(table); |
|
+ *key = UPKEY(table); |
|
+ remove_upacked_entry(table); |
|
+ return 1; |
|
+ } |
|
+ return 0; |
|
+ } |
|
+ |
|
if (table->entries_packed) { |
|
- st_index_t i; |
|
- for (i = 0; i < table->num_entries; i++) { |
|
- if ((st_data_t)table->bins[i*2] == *key) { |
|
- if (value != 0) *value = (st_data_t)table->bins[i*2+1]; |
|
- table->num_entries--; |
|
- memmove(&table->bins[i*2], &table->bins[(i+1)*2], |
|
- sizeof(struct st_table_entry*) * 2*(table->num_entries-i)); |
|
- return 1; |
|
- } |
|
+ st_index_t i = find_packed_index(table, hash_val, *key); |
|
+ if (i < table->real_entries) { |
|
+ if (value != 0) *value = PVAL(table, i); |
|
+ *key = PKEY(table, i); |
|
+ remove_packed_entry(table, i); |
|
+ return 1; |
|
} |
|
if (value != 0) *value = 0; |
|
return 0; |
|
} |
|
|
|
- hash_val = do_hash_bin(*key, table); |
|
- |
|
- for (prev = &table->bins[hash_val]; (ptr = *prev) != 0; prev = &ptr->next) { |
|
+ prev = &table->bins[hash_val % table->num_bins]; |
|
+ for (;(ptr = *prev) != 0; prev = &ptr->next) { |
|
if (EQUAL(table, *key, ptr->key)) { |
|
*prev = ptr->next; |
|
- REMOVE_ENTRY(table, ptr); |
|
+ remove_entry(table, ptr); |
|
if (value != 0) *value = ptr->record; |
|
*key = ptr->key; |
|
- free(ptr); |
|
+ st_free_entry(ptr); |
|
return 1; |
|
} |
|
} |
|
@@ -665,25 +912,36 @@ st_delete_safe(register st_table *table, register st_data_t *key, st_data_t *val |
|
st_index_t hash_val; |
|
register st_table_entry *ptr; |
|
|
|
+ hash_val = do_hash(*key, table); |
|
+ |
|
+ if (ULTRAPACKED(table)) { |
|
+ if (check_upacked(table, hash_val, *key)) { |
|
+ if (value != 0) *value = UPVAL(table); |
|
+ *key = UPKEY(table); |
|
+ remove_safe_upacked_entry(table, never); |
|
+ return 1; |
|
+ } |
|
+ if (value != 0) *value = 0; |
|
+ return 0; |
|
+ } |
|
+ |
|
if (table->entries_packed) { |
|
- st_index_t i; |
|
- for (i = 0; i < table->num_entries; i++) { |
|
- if ((st_data_t)table->bins[i*2] == *key) { |
|
- if (value != 0) *value = (st_data_t)table->bins[i*2+1]; |
|
- table->bins[i*2] = (void *)never; |
|
- return 1; |
|
- } |
|
+ st_index_t i = find_packed_index(table, hash_val, *key); |
|
+ if (i < table->real_entries) { |
|
+ if (value != 0) *value = PVAL(table, i); |
|
+ *key = PKEY(table, i); |
|
+ remove_safe_packed_entry(table, i, never); |
|
+ return 1; |
|
} |
|
if (value != 0) *value = 0; |
|
return 0; |
|
} |
|
|
|
- hash_val = do_hash_bin(*key, table); |
|
- ptr = table->bins[hash_val]; |
|
+ ptr = table->bins[hash_val % table->num_bins]; |
|
|
|
for (; ptr != 0; ptr = ptr->next) { |
|
if ((ptr->key != never) && EQUAL(table, ptr->key, *key)) { |
|
- REMOVE_ENTRY(table, ptr); |
|
+ remove_entry(table, ptr); |
|
*key = ptr->key; |
|
if (value != 0) *value = ptr->record; |
|
ptr->key = ptr->record = never; |
|
@@ -701,17 +959,23 @@ st_cleanup_safe(st_table *table, st_data_t never) |
|
st_table_entry *ptr, **last, *tmp; |
|
st_index_t i; |
|
|
|
+ if (ULTRAPACKED(table)) { |
|
+ table->real_upacked = table->num_entries; |
|
+ return; |
|
+ } |
|
+ |
|
if (table->entries_packed) { |
|
st_index_t i = 0, j = 0; |
|
- while ((st_data_t)table->bins[i*2] != never) { |
|
- if (i++ == table->num_entries) return; |
|
+ while (PKEY(table, i) != never) { |
|
+ if (i++ == table->real_entries) return; |
|
} |
|
- for (j = i; ++i < table->num_entries;) { |
|
- if ((st_data_t)table->bins[i*2] == never) continue; |
|
- table->bins[j*2] = table->bins[i*2]; |
|
- table->bins[j*2+1] = table->bins[i*2+1]; |
|
+ for (j = i; ++i < table->real_entries;) { |
|
+ if (PKEY(table, i) == never) continue; |
|
+ PACKED_ENT(table, j) = PACKED_ENT(table, i); |
|
j++; |
|
} |
|
+ table->real_entries = j; |
|
+ /* table->num_entries really should be equal j at this moment, but let set it anyway */ |
|
table->num_entries = j; |
|
return; |
|
} |
|
@@ -722,7 +986,7 @@ st_cleanup_safe(st_table *table, st_data_t never) |
|
if (ptr->key == never) { |
|
tmp = ptr; |
|
*last = ptr = ptr->next; |
|
- free(tmp); |
|
+ st_free_entry(tmp); |
|
} |
|
else { |
|
ptr = *(last = &ptr->next); |
|
@@ -732,50 +996,78 @@ st_cleanup_safe(st_table *table, st_data_t never) |
|
} |
|
|
|
int |
|
-st_foreach(st_table *table, int (*func)(ANYARGS), st_data_t arg) |
|
+st_foreach_check(st_table *table, int (*func)(ANYARGS), st_data_t arg, st_data_t never) |
|
{ |
|
st_table_entry *ptr, **last, *tmp; |
|
enum st_retval retval; |
|
- st_index_t i; |
|
+ st_data_t key, val; |
|
+ st_index_t hash, i = 0; |
|
+ |
|
+ if (table->num_entries == 0) { |
|
+ return 0; |
|
+ } |
|
+ |
|
+ if (ULTRAPACKED(table)) { |
|
+ key = UPKEY(table); |
|
+ val = UPVAL(table); |
|
+ hash = UPHASH(table); |
|
+ if (key == never) return 0; |
|
+ retval = (*func)(key, val, arg); |
|
+ if (!ULTRAPACKED(table)) { |
|
+ goto packed; |
|
+ } |
|
+ switch(retval) { |
|
+ case ST_CHECK: |
|
+ if (UPHASH(table) == 0 && UPKEY(table) == never) |
|
+ break; |
|
+ if (check_upacked(table, hash, key)) |
|
+ break; |
|
+ goto deleted; |
|
+ case ST_DELETE: |
|
+ remove_safe_upacked_entry(table, never); |
|
+ case ST_CONTINUE: |
|
+ case ST_STOP: |
|
+ break; |
|
+ } |
|
+ return 0; |
|
+ } |
|
|
|
if (table->entries_packed) { |
|
- for (i = 0; i < table->num_entries; i++) { |
|
- st_index_t j; |
|
- st_data_t key, val; |
|
- key = (st_data_t)table->bins[i*2]; |
|
- val = (st_data_t)table->bins[i*2+1]; |
|
- retval = (*func)(key, val, arg); |
|
+ for (i = 0; i < table->real_entries; i++) { |
|
+ key = PKEY(table, i); |
|
+ val = PVAL(table, i); |
|
+ hash = PHASH(table, i); |
|
+ if (key == never) continue; |
|
+ retval = (*func)(key, val, arg); |
|
+ packed: |
|
if (!table->entries_packed) { |
|
- FIND_ENTRY(table, ptr, key, i); |
|
+ FIND_ENTRY(table, ptr, hash, i); |
|
if (retval == ST_CHECK) { |
|
if (!ptr) goto deleted; |
|
goto unpacked_continue; |
|
} |
|
goto unpacked; |
|
} |
|
- switch (retval) { |
|
+ switch (retval) { |
|
case ST_CHECK: /* check if hash is modified during iteration */ |
|
- for (j = 0; j < table->num_entries; j++) { |
|
- if ((st_data_t)table->bins[j*2] == key) |
|
- break; |
|
- } |
|
- if (j == table->num_entries) { |
|
+ if (PHASH(table, i) == 0 && PKEY(table, i) == never) { |
|
+ break; |
|
+ } |
|
+ i = find_packed_index(table, hash, key); |
|
+ if (i == table->real_entries) { |
|
goto deleted; |
|
- } |
|
+ } |
|
/* fall through */ |
|
case ST_CONTINUE: |
|
break; |
|
case ST_STOP: |
|
return 0; |
|
case ST_DELETE: |
|
- table->num_entries--; |
|
- memmove(&table->bins[i*2], &table->bins[(i+1)*2], |
|
- sizeof(struct st_table_entry*) * 2*(table->num_entries-i)); |
|
- i--; |
|
- break; |
|
- } |
|
- } |
|
- return 0; |
|
+ remove_safe_packed_entry(table, i, never); |
|
+ break; |
|
+ } |
|
+ } |
|
+ return 0; |
|
} |
|
else { |
|
ptr = table->head; |
|
@@ -783,6 +1075,8 @@ st_foreach(st_table *table, int (*func)(ANYARGS), st_data_t arg) |
|
|
|
if (ptr != 0) { |
|
do { |
|
+ if (ptr->key == never) |
|
+ goto unpacked_continue; |
|
i = ptr->hash % table->num_bins; |
|
retval = (*func)(ptr->key, ptr->record, arg); |
|
unpacked: |
|
@@ -808,10 +1102,100 @@ st_foreach(st_table *table, int (*func)(ANYARGS), st_data_t arg) |
|
for (; (tmp = *last) != 0; last = &tmp->next) { |
|
if (ptr == tmp) { |
|
tmp = ptr->fore; |
|
+ remove_entry(table, ptr); |
|
+ ptr->key = ptr->record = never; |
|
+ ptr->hash = 0; |
|
+ ptr = tmp; |
|
+ break; |
|
+ } |
|
+ } |
|
+ } |
|
+ } while (ptr && table->head); |
|
+ } |
|
+ return 0; |
|
+} |
|
+ |
|
+int |
|
+st_foreach(st_table *table, int (*func)(ANYARGS), st_data_t arg) |
|
+{ |
|
+ st_table_entry *ptr, **last, *tmp; |
|
+ enum st_retval retval; |
|
+ st_data_t key, val; |
|
+ st_index_t hash, i = 0; |
|
+ |
|
+ if (table->num_entries == 0) { |
|
+ return 0; |
|
+ } |
|
+ |
|
+ if (ULTRAPACKED(table)) { |
|
+ key = UPKEY(table); |
|
+ val = UPVAL(table); |
|
+ hash = UPHASH(table); |
|
+ retval = (*func)(key, val, arg); |
|
+ if (!ULTRAPACKED(table)) { |
|
+ goto packed; |
|
+ } |
|
+ switch (retval) { |
|
+ case ST_DELETE: |
|
+ remove_upacked_entry(table); |
|
+ case ST_CONTINUE: |
|
+ case ST_CHECK: |
|
+ case ST_STOP: |
|
+ break; |
|
+ } |
|
+ return 0; |
|
+ } |
|
+ |
|
+ if (table->entries_packed) { |
|
+ for (i = 0; i < table->real_entries; i++) { |
|
+ key = PKEY(table, i); |
|
+ val = PVAL(table, i); |
|
+ hash = PHASH(table, i); |
|
+ retval = (*func)(key, val, arg); |
|
+ packed: |
|
+ if (!table->entries_packed) { |
|
+ FIND_ENTRY(table, ptr, hash, i); |
|
+ if (!ptr) return 0; |
|
+ goto unpacked; |
|
+ } |
|
+ switch (retval) { |
|
+ case ST_CONTINUE: |
|
+ break; |
|
+ case ST_CHECK: |
|
+ case ST_STOP: |
|
+ return 0; |
|
+ case ST_DELETE: |
|
+ remove_packed_entry(table, i); |
|
+ i--; |
|
+ break; |
|
+ } |
|
+ } |
|
+ return 0; |
|
+ } |
|
+ else { |
|
+ ptr = table->head; |
|
+ } |
|
+ |
|
+ if (ptr != 0) { |
|
+ do { |
|
+ i = ptr->hash % table->num_bins; |
|
+ retval = (*func)(ptr->key, ptr->record, arg); |
|
+ unpacked: |
|
+ switch (retval) { |
|
+ case ST_CONTINUE: |
|
+ ptr = ptr->fore; |
|
+ break; |
|
+ case ST_CHECK: |
|
+ case ST_STOP: |
|
+ return 0; |
|
+ case ST_DELETE: |
|
+ last = &table->bins[ptr->hash % table->num_bins]; |
|
+ for (; (tmp = *last) != 0; last = &tmp->next) { |
|
+ if (ptr == tmp) { |
|
+ tmp = ptr->fore; |
|
*last = ptr->next; |
|
- REMOVE_ENTRY(table, ptr); |
|
- free(ptr); |
|
- if (ptr == tmp) return 0; |
|
+ remove_entry(table, ptr); |
|
+ st_free_entry(ptr); |
|
ptr = tmp; |
|
break; |
|
} |
|
@@ -834,13 +1218,13 @@ st_reverse_foreach(st_table *table, int (*func)(ANYARGS), st_data_t arg) |
|
for (i = table->num_entries-1; 0 <= i; i--) { |
|
int j; |
|
st_data_t key, val; |
|
- key = (st_data_t)table->bins[i*2]; |
|
- val = (st_data_t)table->bins[i*2+1]; |
|
+ key = PKEY(table, i); |
|
+ val = PVAL(table, i); |
|
retval = (*func)(key, val, arg); |
|
switch (retval) { |
|
case ST_CHECK: /* check if hash is modified during iteration */ |
|
for (j = 0; j < table->num_entries; j++) { |
|
- if ((st_data_t)table->bins[j*2] == key) |
|
+ if (PKEY(table, j) == key) |
|
break; |
|
} |
|
if (j == table->num_entries) { |
|
@@ -854,9 +1238,7 @@ st_reverse_foreach(st_table *table, int (*func)(ANYARGS), st_data_t arg) |
|
case ST_STOP: |
|
return 0; |
|
case ST_DELETE: |
|
- table->num_entries--; |
|
- memmove(&table->bins[i*2], &table->bins[(i+1)*2], |
|
- sizeof(struct st_table_entry*) * 2*(table->num_entries-i)); |
|
+ remove_packed_entry(table, i); |
|
break; |
|
} |
|
} |
|
@@ -889,8 +1271,8 @@ st_reverse_foreach(st_table *table, int (*func)(ANYARGS), st_data_t arg) |
|
if (ptr == tmp) { |
|
tmp = ptr->back; |
|
*last = ptr->next; |
|
- REMOVE_ENTRY(table, ptr); |
|
- free(ptr); |
|
+ remove_entry(table, ptr); |
|
+ st_free_entry(ptr); |
|
ptr = tmp; |
|
break; |
|
} |
|
diff --git a/time.c b/time.c |
|
index 755812f..83edc83 100644 |
|
--- a/time.c |
|
+++ b/time.c |
|
@@ -4712,16 +4712,14 @@ time_mload(VALUE time, VALUE str) |
|
long nsec; |
|
VALUE submicro, nano_num, nano_den, offset; |
|
wideval_t timew; |
|
- st_data_t data; |
|
|
|
time_modify(time); |
|
|
|
#define get_attr(attr, iffound) \ |
|
attr = rb_attr_get(str, id_##attr); \ |
|
if (!NIL_P(attr)) { \ |
|
- data = id_##attr; \ |
|
iffound; \ |
|
- st_delete(rb_generic_ivar_table(str), &data, 0); \ |
|
+ sa_delete(rb_generic_ivar_table(str), (sa_index_t)id_##attr, 0); \ |
|
} |
|
|
|
get_attr(nano_num, {}); |
|
diff --git a/variable.c b/variable.c |
|
index 3da500e..385d4af 100644 |
|
--- a/variable.c |
|
+++ b/variable.c |
|
@@ -19,15 +19,15 @@ |
|
#include "constant.h" |
|
#include "internal.h" |
|
|
|
-st_table *rb_global_tbl; |
|
-st_table *rb_class_tbl; |
|
+sa_table rb_global_tbl; |
|
+sa_table rb_class_tbl; |
|
static ID autoload, classpath, tmp_classpath, classid; |
|
|
|
void |
|
Init_var_tables(void) |
|
{ |
|
- rb_global_tbl = st_init_numtable(); |
|
- rb_class_tbl = st_init_numtable(); |
|
+ sa_init_table(&rb_global_tbl, 0); |
|
+ sa_init_table(&rb_class_tbl, 0); |
|
CONST_ID(autoload, "__autoload__"); |
|
CONST_ID(classpath, "__classpath__"); |
|
CONST_ID(tmp_classpath, "__tmp_classpath__"); |
|
@@ -43,7 +43,7 @@ struct fc_result { |
|
}; |
|
|
|
static VALUE |
|
-fc_path(struct fc_result *fc, ID name) |
|
+fc_path(struct fc_result *fc, sa_index_t name) |
|
{ |
|
VALUE path, tmp; |
|
|
|
@@ -51,8 +51,7 @@ fc_path(struct fc_result *fc, ID name) |
|
while (fc) { |
|
st_data_t n; |
|
if (fc->track == rb_cObject) break; |
|
- if (RCLASS_IV_TBL(fc->track) && |
|
- st_lookup(RCLASS_IV_TBL(fc->track), (st_data_t)classpath, &n)) { |
|
+ if (sa_lookup(RCLASS_IV_TBL(fc->track), (sa_index_t)classpath, &n)) { |
|
tmp = rb_str_dup((VALUE)n); |
|
rb_str_cat2(tmp, "::"); |
|
rb_str_append(tmp, path); |
|
@@ -70,7 +69,7 @@ fc_path(struct fc_result *fc, ID name) |
|
} |
|
|
|
static int |
|
-fc_i(ID key, rb_const_entry_t *ce, struct fc_result *res) |
|
+fc_i(sa_index_t key, rb_const_entry_t *ce, struct fc_result *res) |
|
{ |
|
VALUE value = ce->value; |
|
if (!rb_is_const_id(key)) return ST_CONTINUE; |
|
@@ -98,7 +97,7 @@ fc_i(ID key, rb_const_entry_t *ce, struct fc_result *res) |
|
arg.klass = res->klass; |
|
arg.track = value; |
|
arg.prev = res; |
|
- st_foreach(RCLASS_CONST_TBL(value), fc_i, (st_data_t)&arg); |
|
+ sa_foreach(RCLASS_CONST_TBL(value), fc_i, (st_data_t)&arg); |
|
if (arg.path) { |
|
res->path = arg.path; |
|
return ST_STOP; |
|
@@ -123,18 +122,14 @@ find_class_path(VALUE klass) |
|
arg.track = rb_cObject; |
|
arg.prev = 0; |
|
if (RCLASS_CONST_TBL(rb_cObject)) { |
|
- st_foreach_safe(RCLASS_CONST_TBL(rb_cObject), fc_i, (st_data_t)&arg); |
|
+ sa_foreach(RCLASS_CONST_TBL(rb_cObject), fc_i, (st_data_t)&arg); |
|
} |
|
if (arg.path == 0) { |
|
- st_foreach_safe(rb_class_tbl, fc_i, (st_data_t)&arg); |
|
+ sa_foreach(&rb_class_tbl, fc_i, (st_data_t)&arg); |
|
} |
|
if (arg.path) { |
|
- st_data_t tmp = tmp_classpath; |
|
- if (!RCLASS_IV_TBL(klass)) { |
|
- RCLASS_IV_TBL(klass) = st_init_numtable(); |
|
- } |
|
- st_insert(RCLASS_IV_TBL(klass), (st_data_t)classpath, arg.path); |
|
- st_delete(RCLASS_IV_TBL(klass), &tmp, 0); |
|
+ sa_insert(RCLASS_IV_TBL(klass), (sa_index_t)classpath, arg.path); |
|
+ sa_delete(RCLASS_IV_TBL(klass), (sa_index_t)tmp_classpath, 0); |
|
return arg.path; |
|
} |
|
return Qnil; |
|
@@ -147,16 +142,15 @@ classname(VALUE klass) |
|
st_data_t n; |
|
|
|
if (!klass) klass = rb_cObject; |
|
- if (RCLASS_IV_TBL(klass)) { |
|
- if (!st_lookup(RCLASS_IV_TBL(klass), (st_data_t)classpath, &n)) { |
|
- if (!st_lookup(RCLASS_IV_TBL(klass), (st_data_t)classid, &n)) { |
|
+ if (RCLASS_IV_TBL(klass)->num_entries) { |
|
+ if (!sa_lookup(RCLASS_IV_TBL(klass), (sa_index_t)classpath, &n)) { |
|
+ if (!sa_lookup(RCLASS_IV_TBL(klass), (sa_index_t)classid, &n)) { |
|
return find_class_path(klass); |
|
} |
|
path = rb_str_dup(rb_id2str(SYM2ID((VALUE)n))); |
|
OBJ_FREEZE(path); |
|
- st_insert(RCLASS_IV_TBL(klass), (st_data_t)classpath, (st_data_t)path); |
|
- n = classid; |
|
- st_delete(RCLASS_IV_TBL(klass), &n, 0); |
|
+ sa_insert(RCLASS_IV_TBL(klass), (sa_index_t)classpath, (st_data_t)path); |
|
+ sa_delete(RCLASS_IV_TBL(klass), (sa_index_t)classid, 0); |
|
} |
|
else { |
|
path = (VALUE)n; |
|
@@ -192,8 +186,7 @@ rb_class_path(VALUE klass) |
|
st_data_t n = (st_data_t)path; |
|
|
|
if (!NIL_P(path)) return path; |
|
- if (RCLASS_IV_TBL(klass) && st_lookup(RCLASS_IV_TBL(klass), |
|
- (st_data_t)tmp_classpath, &n)) { |
|
+ if (sa_lookup(RCLASS_IV_TBL(klass), (sa_index_t)tmp_classpath, &n)) { |
|
return (VALUE)n; |
|
} |
|
else { |
|
@@ -364,7 +357,7 @@ rb_global_entry(ID id) |
|
struct global_entry *entry; |
|
st_data_t data; |
|
|
|
- if (!st_lookup(rb_global_tbl, (st_data_t)id, &data)) { |
|
+ if (!sa_lookup(&rb_global_tbl, (sa_index_t)id, &data)) { |
|
struct global_variable *var; |
|
entry = ALLOC(struct global_entry); |
|
var = ALLOC(struct global_variable); |
|
@@ -378,7 +371,7 @@ rb_global_entry(ID id) |
|
|
|
var->block_trace = 0; |
|
var->trace = 0; |
|
- st_add_direct(rb_global_tbl, id, (st_data_t)entry); |
|
+ sa_insert(&rb_global_tbl, (sa_index_t)id, (st_data_t)entry); |
|
} |
|
else { |
|
entry = (struct global_entry *)data; |
|
@@ -454,8 +447,8 @@ readonly_setter(VALUE val, ID id, void *data, struct global_variable *gvar) |
|
rb_name_error(id, "%s is a read-only variable", rb_id2name(id)); |
|
} |
|
|
|
-static int |
|
-mark_global_entry(ID key, struct global_entry *entry) |
|
+static void |
|
+mark_global_entry(struct global_entry *entry) |
|
{ |
|
struct trace_var *trace; |
|
struct global_variable *var = entry->var; |
|
@@ -466,14 +459,14 @@ mark_global_entry(ID key, struct global_entry *entry) |
|
if (trace->data) rb_gc_mark_maybe(trace->data); |
|
trace = trace->next; |
|
} |
|
- return ST_CONTINUE; |
|
} |
|
|
|
void |
|
rb_gc_mark_global_tbl(void) |
|
{ |
|
- if (rb_global_tbl) |
|
- st_foreach_safe(rb_global_tbl, mark_global_entry, 0); |
|
+ SA_FOREACH_START(&rb_global_tbl); |
|
+ mark_global_entry((struct global_entry*) value); |
|
+ SA_FOREACH_END(); |
|
} |
|
|
|
static ID |
|
@@ -635,7 +628,7 @@ rb_f_untrace_var(int argc, VALUE *argv) |
|
rb_secure(4); |
|
rb_scan_args(argc, argv, "11", &var, &cmd); |
|
id = rb_to_id(var); |
|
- if (!st_lookup(rb_global_tbl, (st_data_t)id, &data)) { |
|
+ if (!sa_lookup(&rb_global_tbl, (sa_index_t)id, &data)) { |
|
rb_name_error(id, "undefined global variable %s", rb_id2name(id)); |
|
} |
|
|
|
@@ -742,13 +735,6 @@ rb_gvar_defined(struct global_entry *entry) |
|
return Qtrue; |
|
} |
|
|
|
-static int |
|
-gvar_i(ID key, struct global_entry *entry, VALUE ary) |
|
-{ |
|
- rb_ary_push(ary, ID2SYM(key)); |
|
- return ST_CONTINUE; |
|
-} |
|
- |
|
/* |
|
* call-seq: |
|
* global_variables -> array |
|
@@ -765,7 +751,9 @@ rb_f_global_variables(void) |
|
char buf[2]; |
|
int i; |
|
|
|
- st_foreach_safe(rb_global_tbl, gvar_i, ary); |
|
+ SA_FOREACH_START(&rb_global_tbl); |
|
+ rb_ary_push(ary, ID2SYM(entry->key)); |
|
+ SA_FOREACH_END(); |
|
buf[0] = '$'; |
|
for (i = 1; i <= 9; ++i) { |
|
buf[1] = (char)(i + '0'); |
|
@@ -784,10 +772,10 @@ rb_alias_variable(ID name1, ID name2) |
|
rb_raise(rb_eSecurityError, "Insecure: can't alias global variable"); |
|
|
|
entry2 = rb_global_entry(name2); |
|
- if (!st_lookup(rb_global_tbl, (st_data_t)name1, &data1)) { |
|
+ if (!sa_lookup(&rb_global_tbl, (sa_index_t)name1, &data1)) { |
|
entry1 = ALLOC(struct global_entry); |
|
entry1->id = name1; |
|
- st_add_direct(rb_global_tbl, name1, (st_data_t)entry1); |
|
+ sa_insert(&rb_global_tbl, (sa_index_t)name1, (st_data_t)entry1); |
|
} |
|
else if ((entry1 = (struct global_entry *)data1)->var != entry2->var) { |
|
struct global_variable *var = entry1->var; |
|
@@ -815,7 +803,7 @@ rb_alias_variable(ID name1, ID name2) |
|
static int special_generic_ivar = 0; |
|
static st_table *generic_iv_tbl; |
|
|
|
-st_table* |
|
+sa_table* |
|
rb_generic_ivar_table(VALUE obj) |
|
{ |
|
st_data_t tbl; |
|
@@ -823,7 +811,7 @@ rb_generic_ivar_table(VALUE obj) |
|
if (!FL_TEST(obj, FL_EXIVAR)) return 0; |
|
if (!generic_iv_tbl) return 0; |
|
if (!st_lookup(generic_iv_tbl, (st_data_t)obj, &tbl)) return 0; |
|
- return (st_table *)tbl; |
|
+ return (sa_table *)tbl; |
|
} |
|
|
|
static VALUE |
|
@@ -833,7 +821,7 @@ generic_ivar_get(VALUE obj, ID id, int warn) |
|
|
|
if (generic_iv_tbl) { |
|
if (st_lookup(generic_iv_tbl, (st_data_t)obj, &tbl)) { |
|
- if (st_lookup((st_table *)tbl, (st_data_t)id, &val)) { |
|
+ if (sa_lookup((sa_table *)tbl, (sa_index_t)id, &val)) { |
|
return (VALUE)val; |
|
} |
|
} |
|
@@ -847,7 +835,6 @@ generic_ivar_get(VALUE obj, ID id, int warn) |
|
static void |
|
generic_ivar_set(VALUE obj, ID id, VALUE val) |
|
{ |
|
- st_table *tbl; |
|
st_data_t data; |
|
|
|
if (rb_special_const_p(obj)) { |
|
@@ -859,24 +846,22 @@ generic_ivar_set(VALUE obj, ID id, VALUE val) |
|
} |
|
if (!st_lookup(generic_iv_tbl, (st_data_t)obj, &data)) { |
|
FL_SET(obj, FL_EXIVAR); |
|
- tbl = st_init_numtable(); |
|
- st_add_direct(generic_iv_tbl, (st_data_t)obj, (st_data_t)tbl); |
|
- st_add_direct(tbl, (st_data_t)id, (st_data_t)val); |
|
- return; |
|
+ data = (st_data_t)sa_new_table(); |
|
+ st_add_direct(generic_iv_tbl, (st_data_t)obj, data); |
|
} |
|
- st_insert((st_table *)data, (st_data_t)id, (st_data_t)val); |
|
+ sa_insert((sa_table *)data, (sa_index_t)id, (st_data_t)val); |
|
} |
|
|
|
static VALUE |
|
generic_ivar_defined(VALUE obj, ID id) |
|
{ |
|
- st_table *tbl; |
|
+ sa_table *tbl; |
|
st_data_t data; |
|
|
|
if (!generic_iv_tbl) return Qfalse; |
|
if (!st_lookup(generic_iv_tbl, (st_data_t)obj, &data)) return Qfalse; |
|
- tbl = (st_table *)data; |
|
- if (st_lookup(tbl, (st_data_t)id, &data)) { |
|
+ tbl = (sa_table *)data; |
|
+ if (sa_lookup(tbl, (sa_index_t)id, &data)) { |
|
return Qtrue; |
|
} |
|
return Qfalse; |
|
@@ -885,18 +870,18 @@ generic_ivar_defined(VALUE obj, ID id) |
|
static int |
|
generic_ivar_remove(VALUE obj, ID id, st_data_t *valp) |
|
{ |
|
- st_table *tbl; |
|
+ sa_table *tbl; |
|
st_data_t data, key = (st_data_t)id; |
|
int status; |
|
|
|
if (!generic_iv_tbl) return 0; |
|
if (!st_lookup(generic_iv_tbl, (st_data_t)obj, &data)) return 0; |
|
- tbl = (st_table *)data; |
|
- status = st_delete(tbl, &key, valp); |
|
+ tbl = (sa_table *)data; |
|
+ status = sa_delete(tbl, (sa_index_t)id, valp); |
|
if (tbl->num_entries == 0) { |
|
key = (st_data_t)obj; |
|
st_delete(generic_iv_tbl, &key, &data); |
|
- st_free_table((st_table *)data); |
|
+ sa_free_table(tbl); |
|
} |
|
return status; |
|
} |
|
@@ -908,22 +893,17 @@ rb_mark_generic_ivar(VALUE obj) |
|
|
|
if (!generic_iv_tbl) return; |
|
if (st_lookup(generic_iv_tbl, (st_data_t)obj, &tbl)) { |
|
- rb_mark_tbl((st_table *)tbl); |
|
+ rb_mark_sa_tbl((sa_table *)tbl); |
|
} |
|
} |
|
|
|
static int |
|
-givar_mark_i(ID key, VALUE value) |
|
-{ |
|
- rb_gc_mark(value); |
|
- return ST_CONTINUE; |
|
-} |
|
- |
|
-static int |
|
-givar_i(VALUE obj, st_table *tbl) |
|
+givar_i(VALUE obj, sa_table *tbl) |
|
{ |
|
if (rb_special_const_p(obj)) { |
|
- st_foreach_safe(tbl, givar_mark_i, 0); |
|
+ SA_FOREACH_START(tbl); |
|
+ rb_gc_mark((VALUE)value); |
|
+ SA_FOREACH_END(); |
|
} |
|
return ST_CONTINUE; |
|
} |
|
@@ -943,7 +923,7 @@ rb_free_generic_ivar(VALUE obj) |
|
|
|
if (!generic_iv_tbl) return; |
|
if (st_delete(generic_iv_tbl, &key, &tbl)) |
|
- st_free_table((st_table *)tbl); |
|
+ sa_free_table((sa_table *)tbl); |
|
} |
|
|
|
RUBY_FUNC_EXPORTED size_t |
|
@@ -951,7 +931,7 @@ rb_generic_ivar_memsize(VALUE obj) |
|
{ |
|
st_data_t tbl; |
|
if (st_lookup(generic_iv_tbl, (st_data_t)obj, &tbl)) |
|
- return st_memsize((st_table *)tbl); |
|
+ return sa_memsize((sa_table *)tbl); |
|
return 0; |
|
} |
|
|
|
@@ -970,17 +950,17 @@ rb_copy_generic_ivar(VALUE clone, VALUE obj) |
|
return; |
|
} |
|
if (st_lookup(generic_iv_tbl, (st_data_t)obj, &data)) { |
|
- st_table *tbl = (st_table *)data; |
|
+ sa_table *tbl = (sa_table *)data; |
|
|
|
if (tbl->num_entries == 0) |
|
goto clear; |
|
|
|
if (st_lookup(generic_iv_tbl, (st_data_t)clone, &data)) { |
|
- st_free_table((st_table *)data); |
|
- st_insert(generic_iv_tbl, (st_data_t)clone, (st_data_t)st_copy(tbl)); |
|
+ sa_free_table((sa_table *)data); |
|
+ st_insert(generic_iv_tbl, (st_data_t)clone, (st_data_t)sa_copy(tbl)); |
|
} |
|
else { |
|
- st_add_direct(generic_iv_tbl, (st_data_t)clone, (st_data_t)st_copy(tbl)); |
|
+ st_add_direct(generic_iv_tbl, (st_data_t)clone, (st_data_t)sa_copy(tbl)); |
|
FL_SET(clone, FL_EXIVAR); |
|
} |
|
} |
|
@@ -990,7 +970,7 @@ static VALUE |
|
ivar_get(VALUE obj, ID id, int warn) |
|
{ |
|
VALUE val, *ptr; |
|
- struct st_table *iv_index_tbl; |
|
+ sa_table *iv_index_tbl; |
|
long len; |
|
st_data_t index; |
|
|
|
@@ -1000,7 +980,7 @@ ivar_get(VALUE obj, ID id, int warn) |
|
ptr = ROBJECT_IVPTR(obj); |
|
iv_index_tbl = ROBJECT_IV_INDEX_TBL(obj); |
|
if (!iv_index_tbl) break; |
|
- if (!st_lookup(iv_index_tbl, (st_data_t)id, &index)) break; |
|
+ if (!sa_lookup(iv_index_tbl, (st_data_t)id, &index)) break; |
|
if (len <= (long)index) break; |
|
val = ptr[index]; |
|
if (val != Qundef) |
|
@@ -1008,7 +988,7 @@ ivar_get(VALUE obj, ID id, int warn) |
|
break; |
|
case T_CLASS: |
|
case T_MODULE: |
|
- if (RCLASS_IV_TBL(obj) && st_lookup(RCLASS_IV_TBL(obj), (st_data_t)id, &index)) |
|
+ if (sa_lookup(RCLASS_IV_TBL(obj), (sa_index_t)id, &index)) |
|
return (VALUE)index; |
|
break; |
|
default: |
|
@@ -1037,7 +1017,7 @@ rb_attr_get(VALUE obj, ID id) |
|
VALUE |
|
rb_ivar_set(VALUE obj, ID id, VALUE val) |
|
{ |
|
- struct st_table *iv_index_tbl; |
|
+ struct sa_table *iv_index_tbl; |
|
st_data_t index; |
|
long i, len; |
|
int ivar_extended; |
|
@@ -1051,14 +1031,11 @@ rb_ivar_set(VALUE obj, ID id, VALUE val) |
|
if (!iv_index_tbl) { |
|
VALUE klass = rb_obj_class(obj); |
|
iv_index_tbl = RCLASS_IV_INDEX_TBL(klass); |
|
- if (!iv_index_tbl) { |
|
- iv_index_tbl = RCLASS_IV_INDEX_TBL(klass) = st_init_numtable(); |
|
- } |
|
} |
|
ivar_extended = 0; |
|
- if (!st_lookup(iv_index_tbl, (st_data_t)id, &index)) { |
|
+ if (!sa_lookup(iv_index_tbl, (sa_index_t)id, &index)) { |
|
index = iv_index_tbl->num_entries; |
|
- st_add_direct(iv_index_tbl, (st_data_t)id, index); |
|
+ sa_insert(iv_index_tbl, (sa_index_t)id, index); |
|
ivar_extended = 1; |
|
} |
|
len = ROBJECT_NUMIV(obj); |
|
@@ -1098,8 +1075,7 @@ rb_ivar_set(VALUE obj, ID id, VALUE val) |
|
break; |
|
case T_CLASS: |
|
case T_MODULE: |
|
- if (!RCLASS_IV_TBL(obj)) RCLASS_IV_TBL(obj) = st_init_numtable(); |
|
- st_insert(RCLASS_IV_TBL(obj), (st_data_t)id, val); |
|
+ sa_insert(RCLASS_IV_TBL(obj), (sa_index_t)id, val); |
|
break; |
|
default: |
|
generic_ivar_set(obj, id, val); |
|
@@ -1112,13 +1088,13 @@ VALUE |
|
rb_ivar_defined(VALUE obj, ID id) |
|
{ |
|
VALUE val; |
|
- struct st_table *iv_index_tbl; |
|
+ struct sa_table *iv_index_tbl; |
|
st_data_t index; |
|
switch (TYPE(obj)) { |
|
case T_OBJECT: |
|
iv_index_tbl = ROBJECT_IV_INDEX_TBL(obj); |
|
if (!iv_index_tbl) break; |
|
- if (!st_lookup(iv_index_tbl, (st_data_t)id, &index)) break; |
|
+ if (!sa_lookup(iv_index_tbl, (sa_index_t)id, &index)) break; |
|
if (ROBJECT_NUMIV(obj) <= (long)index) break; |
|
val = ROBJECT_IVPTR(obj)[index]; |
|
if (val != Qundef) |
|
@@ -1126,7 +1102,7 @@ rb_ivar_defined(VALUE obj, ID id) |
|
break; |
|
case T_CLASS: |
|
case T_MODULE: |
|
- if (RCLASS_IV_TBL(obj) && st_lookup(RCLASS_IV_TBL(obj), (st_data_t)id, 0)) |
|
+ if (sa_lookup(RCLASS_IV_TBL(obj), (sa_index_t)id, 0)) |
|
return Qtrue; |
|
break; |
|
default: |
|
@@ -1137,40 +1113,26 @@ rb_ivar_defined(VALUE obj, ID id) |
|
return Qfalse; |
|
} |
|
|
|
-struct obj_ivar_tag { |
|
- VALUE obj; |
|
- int (*func)(ID key, VALUE val, st_data_t arg); |
|
- st_data_t arg; |
|
-}; |
|
- |
|
-static int |
|
-obj_ivar_i(st_data_t key, st_data_t index, st_data_t arg) |
|
-{ |
|
- struct obj_ivar_tag *data = (struct obj_ivar_tag *)arg; |
|
- if ((long)index < ROBJECT_NUMIV(data->obj)) { |
|
- VALUE val = ROBJECT_IVPTR(data->obj)[(long)index]; |
|
- if (val != Qundef) { |
|
- return (data->func)((ID)key, val, data->arg); |
|
- } |
|
- } |
|
- return ST_CONTINUE; |
|
-} |
|
- |
|
static void |
|
obj_ivar_each(VALUE obj, int (*func)(ANYARGS), st_data_t arg) |
|
{ |
|
- st_table *tbl; |
|
- struct obj_ivar_tag data; |
|
+ sa_table *tbl; |
|
+ long numiv = ROBJECT_NUMIV(obj); |
|
+ VALUE *vars = ROBJECT_IVPTR(obj); |
|
|
|
tbl = ROBJECT_IV_INDEX_TBL(obj); |
|
- if (!tbl) |
|
+ if (!tbl || !numiv) |
|
return; |
|
|
|
- data.obj = obj; |
|
- data.func = (int (*)(ID key, VALUE val, st_data_t arg))func; |
|
- data.arg = arg; |
|
- |
|
- st_foreach_safe(tbl, obj_ivar_i, (st_data_t)&data); |
|
+ SA_FOREACH_START(tbl); |
|
+ if ((long)value < numiv) { |
|
+ VALUE val = vars[value]; |
|
+ if (val != Qundef) { |
|
+ if (((sa_iter_func)func)(entry->key, val, arg) == SA_STOP) |
|
+ break; |
|
+ } |
|
+ } |
|
+ SA_FOREACH_END(); |
|
} |
|
|
|
void |
|
@@ -1182,9 +1144,7 @@ rb_ivar_foreach(VALUE obj, int (*func)(ANYARGS), st_data_t arg) |
|
break; |
|
case T_CLASS: |
|
case T_MODULE: |
|
- if (RCLASS_IV_TBL(obj)) { |
|
- st_foreach_safe(RCLASS_IV_TBL(obj), func, arg); |
|
- } |
|
+ sa_foreach(RCLASS_IV_TBL(obj), func, arg); |
|
break; |
|
default: |
|
if (!generic_iv_tbl) break; |
|
@@ -1192,7 +1152,7 @@ rb_ivar_foreach(VALUE obj, int (*func)(ANYARGS), st_data_t arg) |
|
st_data_t tbl; |
|
|
|
if (st_lookup(generic_iv_tbl, (st_data_t)obj, &tbl)) { |
|
- st_foreach_safe((st_table *)tbl, func, arg); |
|
+ sa_foreach((sa_table *)tbl, func, arg); |
|
} |
|
} |
|
break; |
|
@@ -1202,11 +1162,11 @@ rb_ivar_foreach(VALUE obj, int (*func)(ANYARGS), st_data_t arg) |
|
st_index_t |
|
rb_ivar_count(VALUE obj) |
|
{ |
|
- st_table *tbl; |
|
+ sa_table *tbl; |
|
switch (TYPE(obj)) { |
|
case T_OBJECT: |
|
if ((tbl = ROBJECT_IV_INDEX_TBL(obj)) != 0) { |
|
- st_index_t i, count, num = tbl->num_entries; |
|
+ sa_index_t i, count, num = tbl->num_entries; |
|
const VALUE *const ivptr = ROBJECT_IVPTR(obj); |
|
for (i = count = 0; i < num; ++i) { |
|
if (ivptr[i] != Qundef) { |
|
@@ -1228,7 +1188,7 @@ rb_ivar_count(VALUE obj) |
|
st_data_t data; |
|
|
|
if (st_lookup(generic_iv_tbl, (st_data_t)obj, &data) && |
|
- (tbl = (st_table *)data) != 0) { |
|
+ (tbl = (sa_table *)data) != 0) { |
|
return tbl->num_entries; |
|
} |
|
} |
|
@@ -1238,7 +1198,7 @@ rb_ivar_count(VALUE obj) |
|
} |
|
|
|
static int |
|
-ivar_i(ID key, VALUE val, VALUE ary) |
|
+ivar_i(sa_index_t key, VALUE val, VALUE ary) |
|
{ |
|
if (rb_is_instance_id(key)) { |
|
rb_ary_push(ary, ID2SYM(key)); |
|
@@ -1301,7 +1261,7 @@ rb_obj_remove_instance_variable(VALUE obj, VALUE name) |
|
VALUE val = Qnil; |
|
const ID id = rb_to_id(name); |
|
st_data_t n, v; |
|
- struct st_table *iv_index_tbl; |
|
+ struct sa_table *iv_index_tbl; |
|
st_data_t index; |
|
|
|
if (!OBJ_UNTRUSTED(obj) && rb_safe_level() >= 4) |
|
@@ -1315,7 +1275,7 @@ rb_obj_remove_instance_variable(VALUE obj, VALUE name) |
|
case T_OBJECT: |
|
iv_index_tbl = ROBJECT_IV_INDEX_TBL(obj); |
|
if (!iv_index_tbl) break; |
|
- if (!st_lookup(iv_index_tbl, (st_data_t)id, &index)) break; |
|
+ if (!sa_lookup(iv_index_tbl, (sa_index_t)id, &index)) break; |
|
if (ROBJECT_NUMIV(obj) <= (long)index) break; |
|
val = ROBJECT_IVPTR(obj)[index]; |
|
if (val != Qundef) { |
|
@@ -1326,14 +1286,14 @@ rb_obj_remove_instance_variable(VALUE obj, VALUE name) |
|
case T_CLASS: |
|
case T_MODULE: |
|
n = id; |
|
- if (RCLASS_IV_TBL(obj) && st_delete(RCLASS_IV_TBL(obj), &n, &v)) { |
|
+ if (sa_delete(RCLASS_IV_TBL(obj), (sa_index_t)id, &v)) { |
|
return (VALUE)v; |
|
} |
|
break; |
|
default: |
|
if (FL_TEST(obj, FL_EXIVAR) || rb_special_const_p(obj)) { |
|
v = val; |
|
- if (generic_ivar_remove(obj, (st_data_t)id, &v)) { |
|
+ if (generic_ivar_remove(obj, id, &v)) { |
|
return (VALUE)v; |
|
} |
|
} |
|
@@ -1410,20 +1370,20 @@ rb_mod_const_missing(VALUE klass, VALUE name) |
|
static void |
|
autoload_mark(void *ptr) |
|
{ |
|
- rb_mark_tbl((st_table *)ptr); |
|
+ rb_mark_sa_tbl((sa_table *)ptr); |
|
} |
|
|
|
static void |
|
autoload_free(void *ptr) |
|
{ |
|
- st_free_table((st_table *)ptr); |
|
+ sa_free_table((sa_table *)ptr); |
|
} |
|
|
|
static size_t |
|
autoload_memsize(const void *ptr) |
|
{ |
|
- const st_table *tbl = ptr; |
|
- return st_memsize(tbl); |
|
+ const sa_table *tbl = ptr; |
|
+ return sa_memsize(tbl); |
|
} |
|
|
|
static const rb_data_type_t autoload_data_type = { |
|
@@ -1432,14 +1392,14 @@ static const rb_data_type_t autoload_data_type = { |
|
}; |
|
|
|
#define check_autoload_table(av) \ |
|
- (struct st_table *)rb_check_typeddata((av), &autoload_data_type) |
|
+ (struct sa_table *)rb_check_typeddata((av), &autoload_data_type) |
|
|
|
void |
|
rb_autoload(VALUE mod, ID id, const char *file) |
|
{ |
|
st_data_t av; |
|
VALUE fn; |
|
- struct st_table *tbl; |
|
+ struct sa_table *tbl; |
|
|
|
if (!rb_is_const_id(id)) { |
|
rb_raise(rb_eNameError, "autoload must be constant name: %s", rb_id2name(id)); |
|
@@ -1448,43 +1408,41 @@ rb_autoload(VALUE mod, ID id, const char *file) |
|
rb_raise(rb_eArgError, "empty file name"); |
|
} |
|
|
|
- if ((tbl = RCLASS_CONST_TBL(mod)) && st_lookup(tbl, (st_data_t)id, &av) && ((rb_const_entry_t*)av)->value != Qundef) |
|
+ if (sa_lookup(RCLASS_CONST_TBL(mod), (sa_index_t)id, &av) && ((rb_const_entry_t*)av)->value != Qundef) |
|
return; |
|
|
|
rb_const_set(mod, id, Qundef); |
|
tbl = RCLASS_IV_TBL(mod); |
|
- if (tbl && st_lookup(tbl, (st_data_t)autoload, &av)) { |
|
+ if (sa_lookup(tbl, (st_data_t)autoload, &av)) { |
|
tbl = check_autoload_table((VALUE)av); |
|
} |
|
else { |
|
- if (!tbl) tbl = RCLASS_IV_TBL(mod) = st_init_numtable(); |
|
av = (st_data_t)TypedData_Wrap_Struct(0, &autoload_data_type, 0); |
|
- st_add_direct(tbl, (st_data_t)autoload, av); |
|
- DATA_PTR(av) = tbl = st_init_numtable(); |
|
+ sa_insert(tbl, (sa_index_t)autoload, av); |
|
+ DATA_PTR(av) = tbl = sa_new_table(); |
|
} |
|
fn = rb_str_new2(file); |
|
FL_UNSET(fn, FL_TAINT); |
|
OBJ_FREEZE(fn); |
|
- st_insert(tbl, (st_data_t)id, (st_data_t)rb_node_newnode(NODE_MEMO, fn, rb_safe_level(), 0)); |
|
+ sa_insert(tbl, (sa_index_t)id, (st_data_t)rb_node_newnode(NODE_MEMO, fn, rb_safe_level(), 0)); |
|
} |
|
|
|
static NODE* |
|
autoload_delete(VALUE mod, ID id) |
|
{ |
|
- st_data_t val, load = 0, n = id; |
|
+ st_data_t val, load = 0; |
|
rb_const_entry_t *ce; |
|
|
|
- st_delete(RCLASS_CONST_TBL(mod), &n, &val); |
|
+ sa_delete(RCLASS_CONST_TBL(mod), (sa_index_t)id, &val); |
|
ce = (rb_const_entry_t*)val; |
|
if (ce) xfree(ce); |
|
- if (st_lookup(RCLASS_IV_TBL(mod), (st_data_t)autoload, &val)) { |
|
- struct st_table *tbl = check_autoload_table((VALUE)val); |
|
+ if (sa_lookup(RCLASS_IV_TBL(mod), (sa_index_t)autoload, &val)) { |
|
+ struct sa_table *tbl = check_autoload_table((VALUE)val); |
|
|
|
- st_delete(tbl, &n, &load); |
|
+ sa_delete(tbl, (sa_index_t)id, &load); |
|
|
|
if (tbl->num_entries == 0) { |
|
- n = autoload; |
|
- st_delete(RCLASS_IV_TBL(mod), &n, &val); |
|
+ sa_delete(RCLASS_IV_TBL(mod), (sa_index_t)autoload, &val); |
|
} |
|
} |
|
|
|
@@ -1509,14 +1467,14 @@ static NODE * |
|
autoload_node(VALUE mod, ID id, const char **loadingpath) |
|
{ |
|
VALUE file; |
|
- struct st_table *tbl; |
|
+ struct sa_table *tbl; |
|
st_data_t val; |
|
NODE *load; |
|
const char *loading; |
|
int safe; |
|
|
|
- if (!st_lookup(RCLASS_IV_TBL(mod), autoload, &val) || |
|
- !(tbl = check_autoload_table((VALUE)val)) || !st_lookup(tbl, (st_data_t)id, &val)) { |
|
+ if (!sa_lookup(RCLASS_IV_TBL(mod), (sa_index_t)autoload, &val) || |
|
+ !(tbl = check_autoload_table((VALUE)val)) || !sa_lookup(tbl, (sa_index_t)id, &val)) { |
|
return 0; |
|
} |
|
load = (NODE *)val; |
|
@@ -1541,10 +1499,10 @@ autoload_node(VALUE mod, ID id, const char **loadingpath) |
|
static int |
|
autoload_node_id(VALUE mod, ID id) |
|
{ |
|
- struct st_table *tbl = RCLASS_CONST_TBL(mod); |
|
+ struct sa_table *tbl = RCLASS_CONST_TBL(mod); |
|
st_data_t val; |
|
|
|
- if (!tbl || !st_lookup(tbl, (st_data_t)id, &val) || ((rb_const_entry_t*)val)->value != Qundef) { |
|
+ if (!tbl || !sa_lookup(tbl, (sa_index_t)id, &val) || ((rb_const_entry_t*)val)->value != Qundef) { |
|
return 0; |
|
} |
|
return 1; |
|
@@ -1593,7 +1551,7 @@ rb_const_get_0(VALUE klass, ID id, int exclude, int recurse, int visibility) |
|
while (RTEST(tmp)) { |
|
VALUE am = 0; |
|
st_data_t data; |
|
- while (RCLASS_CONST_TBL(tmp) && st_lookup(RCLASS_CONST_TBL(tmp), (st_data_t)id, &data)) { |
|
+ while (sa_lookup(RCLASS_CONST_TBL(tmp), (sa_index_t)id, &data)) { |
|
rb_const_entry_t *ce = (rb_const_entry_t *)data; |
|
if (visibility && ce->flag == CONST_PRIVATE) { |
|
rb_name_error(id, "private constant %s::%s referenced", rb_class2name(klass), rb_id2name(id)); |
|
@@ -1686,12 +1644,12 @@ VALUE |
|
rb_const_remove(VALUE mod, ID id) |
|
{ |
|
VALUE val; |
|
- st_data_t v, n = id; |
|
+ st_data_t v; |
|
|
|
if (!OBJ_UNTRUSTED(mod) && rb_safe_level() >= 4) |
|
rb_raise(rb_eSecurityError, "Insecure: can't remove constant"); |
|
rb_check_frozen(mod); |
|
- if (!RCLASS_CONST_TBL(mod) || !st_delete(RCLASS_CONST_TBL(mod), &n, &v)) { |
|
+ if (!sa_delete(RCLASS_CONST_TBL(mod), (sa_index_t)id, &v)) { |
|
if (rb_const_defined_at(mod, id)) { |
|
rb_name_error(id, "cannot remove %s::%s", |
|
rb_class2name(mod), rb_id2name(id)); |
|
@@ -1711,27 +1669,20 @@ rb_const_remove(VALUE mod, ID id) |
|
return val; |
|
} |
|
|
|
-static int |
|
-sv_i(ID key, rb_const_entry_t *ce, st_table *tbl) |
|
-{ |
|
- if (rb_is_const_id(key)) { |
|
- if (!st_lookup(tbl, (st_data_t)key, 0)) { |
|
- st_insert(tbl, (st_data_t)key, (st_data_t)ce); |
|
- } |
|
- } |
|
- return ST_CONTINUE; |
|
-} |
|
- |
|
void* |
|
rb_mod_const_at(VALUE mod, void *data) |
|
{ |
|
- st_table *tbl = data; |
|
+ sa_table *tbl = data; |
|
if (!tbl) { |
|
- tbl = st_init_numtable(); |
|
+ tbl = sa_new_table(); |
|
} |
|
- if (RCLASS_CONST_TBL(mod)) { |
|
- st_foreach_safe(RCLASS_CONST_TBL(mod), sv_i, (st_data_t)tbl); |
|
+ SA_FOREACH_START(RCLASS_CONST_TBL(mod)); |
|
+ if (rb_is_const_id(entry->key)) { |
|
+ if (!sa_lookup(tbl, entry->key, 0)) { |
|
+ sa_insert(tbl, entry->key, value); |
|
+ } |
|
} |
|
+ SA_FOREACH_END(); |
|
return tbl; |
|
} |
|
|
|
@@ -1748,25 +1699,21 @@ rb_mod_const_of(VALUE mod, void *data) |
|
return data; |
|
} |
|
|
|
-static int |
|
-list_i(st_data_t key, st_data_t value, VALUE ary) |
|
-{ |
|
- ID sym = (ID)key; |
|
- rb_const_entry_t *ce = (rb_const_entry_t *)value; |
|
- if (ce->flag != CONST_PRIVATE) rb_ary_push(ary, ID2SYM(sym)); |
|
- return ST_CONTINUE; |
|
-} |
|
- |
|
VALUE |
|
rb_const_list(void *data) |
|
{ |
|
- st_table *tbl = data; |
|
+ sa_table *tbl = data; |
|
VALUE ary; |
|
|
|
if (!tbl) return rb_ary_new2(0); |
|
ary = rb_ary_new2(tbl->num_entries); |
|
- st_foreach_safe(tbl, list_i, ary); |
|
- st_free_table(tbl); |
|
+ |
|
+ SA_FOREACH_START(tbl); |
|
+ rb_const_entry_t *ce = (rb_const_entry_t *)value; |
|
+ if (ce->flag != CONST_PRIVATE) rb_ary_push(ary, ID2SYM(entry->key)); |
|
+ SA_FOREACH_END(); |
|
+ |
|
+ sa_free_table(tbl); |
|
|
|
return ary; |
|
} |
|
@@ -1790,7 +1737,7 @@ VALUE |
|
rb_mod_constants(int argc, VALUE *argv, VALUE mod) |
|
{ |
|
VALUE inherit; |
|
- st_table *tbl; |
|
+ sa_table *tbl; |
|
|
|
if (argc == 0) { |
|
inherit = Qtrue; |
|
@@ -1817,7 +1764,7 @@ rb_const_defined_0(VALUE klass, ID id, int exclude, int recurse, int visibility) |
|
tmp = klass; |
|
retry: |
|
while (tmp) { |
|
- if (RCLASS_CONST_TBL(tmp) && st_lookup(RCLASS_CONST_TBL(tmp), (st_data_t)id, &value)) { |
|
+ if (sa_lookup(RCLASS_CONST_TBL(tmp), (sa_index_t)id, &value)) { |
|
rb_const_entry_t *ce = (rb_const_entry_t *)value; |
|
if (visibility && ce->flag == CONST_PRIVATE) { |
|
return (int)Qfalse; |
|
@@ -1885,6 +1832,7 @@ void |
|
rb_const_set(VALUE klass, ID id, VALUE val) |
|
{ |
|
rb_const_entry_t *ce; |
|
+ st_data_t value; |
|
VALUE visibility = CONST_PUBLIC; |
|
|
|
if (NIL_P(klass)) { |
|
@@ -1893,21 +1841,14 @@ rb_const_set(VALUE klass, ID id, VALUE val) |
|
} |
|
|
|
check_before_mod_set(klass, id, val, "constant"); |
|
- if (!RCLASS_CONST_TBL(klass)) { |
|
- RCLASS_CONST_TBL(klass) = st_init_numtable(); |
|
- } |
|
- else { |
|
- st_data_t value; |
|
- |
|
- if (st_lookup(RCLASS_CONST_TBL(klass), (st_data_t)id, &value)) { |
|
- rb_const_entry_t *ce = (rb_const_entry_t*)value; |
|
- if (ce->value == Qundef) |
|
- autoload_delete(klass, id); |
|
- else { |
|
- visibility = ce->flag; |
|
- rb_warn("already initialized constant %s", rb_id2name(id)); |
|
- } |
|
- } |
|
+ if (sa_lookup(RCLASS_CONST_TBL(klass), (sa_index_t)id, &value)) { |
|
+ rb_const_entry_t *ce = (rb_const_entry_t*)value; |
|
+ if (ce->value == Qundef) |
|
+ autoload_delete(klass, id); |
|
+ else { |
|
+ visibility = ce->flag; |
|
+ rb_warn("already initialized constant %s", rb_id2name(id)); |
|
+ } |
|
} |
|
|
|
rb_vm_change_state(); |
|
@@ -1916,7 +1857,7 @@ rb_const_set(VALUE klass, ID id, VALUE val) |
|
ce->flag = (rb_const_flag_t)visibility; |
|
ce->value = val; |
|
|
|
- st_insert(RCLASS_CONST_TBL(klass), (st_data_t)id, (st_data_t)ce); |
|
+ sa_insert(RCLASS_CONST_TBL(klass), (sa_index_t)id, (st_data_t)ce); |
|
} |
|
|
|
void |
|
@@ -1958,8 +1899,7 @@ set_const_visibility(VALUE mod, int argc, VALUE *argv, rb_const_flag_t flag) |
|
for (i = 0; i < argc; i++) { |
|
VALUE val = argv[i]; |
|
id = rb_to_id(val); |
|
- if (RCLASS_CONST_TBL(mod) && |
|
- st_lookup(RCLASS_CONST_TBL(mod), (st_data_t)id, &v)) { |
|
+ if (sa_lookup(RCLASS_CONST_TBL(mod), (sa_index_t)id, &v)) { |
|
((rb_const_entry_t*)v)->flag = flag; |
|
} |
|
else { |
|
@@ -2008,7 +1948,7 @@ original_module(VALUE c) |
|
} |
|
|
|
#define CVAR_LOOKUP(v,r) do {\ |
|
- if (RCLASS_IV_TBL(klass) && st_lookup(RCLASS_IV_TBL(klass),(st_data_t)id,(v))) {\ |
|
+ if (sa_lookup(RCLASS_IV_TBL(klass),(sa_index_t)id,(v))) {\ |
|
r;\ |
|
}\ |
|
if (FL_TEST(klass, FL_SINGLETON) ) {\ |
|
@@ -2027,7 +1967,7 @@ original_module(VALUE c) |
|
klass = RCLASS_SUPER(klass);\ |
|
}\ |
|
while (klass) {\ |
|
- if (RCLASS_IV_TBL(klass) && st_lookup(RCLASS_IV_TBL(klass),(st_data_t)id,(v))) {\ |
|
+ if (sa_lookup(RCLASS_IV_TBL(klass),(sa_index_t)id,(v))) {\ |
|
r;\ |
|
}\ |
|
klass = RCLASS_SUPER(klass);\ |
|
@@ -2043,15 +1983,13 @@ rb_cvar_set(VALUE klass, ID id, VALUE val) |
|
CVAR_LOOKUP(0, {if (!front) front = klass; target = klass;}); |
|
if (target) { |
|
if (front && target != front) { |
|
- st_data_t did = id; |
|
- |
|
if (RTEST(ruby_verbose)) { |
|
rb_warning("class variable %s of %s is overtaken by %s", |
|
rb_id2name(id), rb_class2name(original_module(front)), |
|
rb_class2name(original_module(target))); |
|
} |
|
if (BUILTIN_TYPE(front) == T_CLASS) { |
|
- st_delete(RCLASS_IV_TBL(front),&did,0); |
|
+ sa_delete(RCLASS_IV_TBL(front),(sa_index_t)id,0); |
|
} |
|
} |
|
} |
|
@@ -2060,11 +1998,7 @@ rb_cvar_set(VALUE klass, ID id, VALUE val) |
|
} |
|
|
|
check_before_mod_set(target, id, val, "class variable"); |
|
- if (!RCLASS_IV_TBL(target)) { |
|
- RCLASS_IV_TBL(target) = st_init_numtable(); |
|
- } |
|
- |
|
- st_insert(RCLASS_IV_TBL(target), (st_data_t)id, (st_data_t)val); |
|
+ sa_insert(RCLASS_IV_TBL(target), (sa_index_t)id, (st_data_t)val); |
|
} |
|
|
|
VALUE |
|
@@ -2080,15 +2014,13 @@ rb_cvar_get(VALUE klass, ID id) |
|
rb_id2name(id), rb_class2name(tmp)); |
|
} |
|
if (front && target != front) { |
|
- st_data_t did = id; |
|
- |
|
if (RTEST(ruby_verbose)) { |
|
rb_warning("class variable %s of %s is overtaken by %s", |
|
rb_id2name(id), rb_class2name(original_module(front)), |
|
rb_class2name(original_module(target))); |
|
} |
|
if (BUILTIN_TYPE(front) == T_CLASS) { |
|
- st_delete(RCLASS_IV_TBL(front),&did,0); |
|
+ sa_delete(RCLASS_IV_TBL(front),(sa_index_t)id,0); |
|
} |
|
} |
|
return (VALUE)value; |
|
@@ -2134,7 +2066,7 @@ rb_define_class_variable(VALUE klass, const char *name, VALUE val) |
|
} |
|
|
|
static int |
|
-cv_i(ID key, VALUE value, VALUE ary) |
|
+cv_i(sa_index_t key, VALUE value, VALUE ary) |
|
{ |
|
if (rb_is_class_id(key)) { |
|
VALUE kval = ID2SYM(key); |
|
@@ -2167,7 +2099,7 @@ rb_mod_class_variables(VALUE obj) |
|
VALUE ary = rb_ary_new(); |
|
|
|
if (RCLASS_IV_TBL(obj)) { |
|
- st_foreach_safe(RCLASS_IV_TBL(obj), cv_i, ary); |
|
+ sa_foreach(RCLASS_IV_TBL(obj), cv_i, ary); |
|
} |
|
return ary; |
|
} |
|
@@ -2196,7 +2128,7 @@ VALUE |
|
rb_mod_remove_cvar(VALUE mod, VALUE name) |
|
{ |
|
const ID id = rb_to_id(name); |
|
- st_data_t val, n = id; |
|
+ st_data_t val; |
|
|
|
if (!rb_is_class_id(id)) { |
|
rb_name_error(id, "wrong class variable name %s", rb_id2name(id)); |
|
@@ -2204,7 +2136,7 @@ rb_mod_remove_cvar(VALUE mod, VALUE name) |
|
if (!OBJ_UNTRUSTED(mod) && rb_safe_level() >= 4) |
|
rb_raise(rb_eSecurityError, "Insecure: can't remove class variable"); |
|
rb_check_frozen(mod); |
|
- if (RCLASS_IV_TBL(mod) && st_delete(RCLASS_IV_TBL(mod), &n, &val)) { |
|
+ if (RCLASS_IV_TBL(mod) && sa_delete(RCLASS_IV_TBL(mod), (sa_index_t)id, &val)) { |
|
return (VALUE)val; |
|
} |
|
if (rb_cvar_defined(mod, id)) { |
|
diff --git a/vm.c b/vm.c |
|
index 3d7b76e..f333088 100644 |
|
--- a/vm.c |
|
+++ b/vm.c |
|
@@ -1042,7 +1042,7 @@ static void |
|
add_opt_method(VALUE klass, ID mid, VALUE bop) |
|
{ |
|
rb_method_entry_t *me; |
|
- if (st_lookup(RCLASS_M_TBL(klass), mid, (void *)&me) && me->def && |
|
+ if (sa_lookup(RCLASS_M_TBL(klass), (sa_index_t)mid, (void *)&me) && me->def && |
|
me->def->type == VM_METHOD_TYPE_CFUNC) { |
|
st_insert(vm_opt_method_table, (st_data_t)me, (st_data_t)bop); |
|
} |
|
diff --git a/vm_insnhelper.c b/vm_insnhelper.c |
|
index 55152df..deeed9b 100644 |
|
--- a/vm_insnhelper.c |
|
+++ b/vm_insnhelper.c |
|
@@ -1187,7 +1187,7 @@ vm_get_ev_const(rb_thread_t *th, const rb_iseq_t *iseq, |
|
st_data_t data; |
|
search_continue: |
|
if (RCLASS_CONST_TBL(klass) && |
|
- st_lookup(RCLASS_CONST_TBL(klass), id, &data)) { |
|
+ sa_lookup(RCLASS_CONST_TBL(klass), (sa_index_t)id, &data)) { |
|
val = ((rb_const_entry_t*)data)->value; |
|
if (val == Qundef) { |
|
if (am == klass) break; |
|
@@ -1297,10 +1297,10 @@ vm_getivar(VALUE obj, ID id, IC ic) |
|
st_data_t index; |
|
long len = ROBJECT_NUMIV(obj); |
|
VALUE *ptr = ROBJECT_IVPTR(obj); |
|
- struct st_table *iv_index_tbl = ROBJECT_IV_INDEX_TBL(obj); |
|
+ struct sa_table *iv_index_tbl = ROBJECT_IV_INDEX_TBL(obj); |
|
|
|
if (iv_index_tbl) { |
|
- if (st_lookup(iv_index_tbl, id, &index)) { |
|
+ if (sa_lookup(iv_index_tbl, (sa_index_t)id, &index)) { |
|
if ((long)index < len) { |
|
val = ptr[index]; |
|
} |
|
@@ -1350,9 +1350,9 @@ vm_setivar(VALUE obj, ID id, VALUE val, IC ic) |
|
} |
|
} |
|
else { |
|
- struct st_table *iv_index_tbl = ROBJECT_IV_INDEX_TBL(obj); |
|
+ struct sa_table *iv_index_tbl = ROBJECT_IV_INDEX_TBL(obj); |
|
|
|
- if (iv_index_tbl && st_lookup(iv_index_tbl, (st_data_t)id, &index)) { |
|
+ if (iv_index_tbl && sa_lookup(iv_index_tbl, (sa_index_t)id, &index)) { |
|
ic->ic_class = klass; |
|
ic->ic_value.index = index; |
|
ic->ic_vmstat = GET_VM_STATE_VERSION(); |
|
diff --git a/vm_method.c b/vm_method.c |
|
index 6e33603..6567440 100644 |
|
--- a/vm_method.c |
|
+++ b/vm_method.c |
|
@@ -2,38 +2,33 @@ |
|
* This file is included by vm.c |
|
*/ |
|
|
|
-#define CACHE_SIZE 0x800 |
|
-#define CACHE_MASK 0x7ff |
|
-#define EXPR1(c,m) ((((c)>>3)^(m))&CACHE_MASK) |
|
- |
|
static void rb_vm_check_redefinition_opt_method(const rb_method_entry_t *me); |
|
|
|
static ID object_id, respond_to_missing; |
|
static ID removed, singleton_removed, undefined, singleton_undefined; |
|
static ID added, singleton_added, attached; |
|
|
|
-struct cache_entry { /* method hash table. */ |
|
- VALUE filled_version; /* filled state version */ |
|
- ID mid; /* method's id */ |
|
- VALUE klass; /* receiver's class */ |
|
- rb_method_entry_t *me; |
|
-}; |
|
- |
|
-static struct cache_entry cache[CACHE_SIZE]; |
|
#define ruby_running (GET_VM()->running) |
|
/* int ruby_running = 0; */ |
|
|
|
+static int |
|
+methods_cache_clear_callback(void *vstart, void *vend, size_t stride, void *data) |
|
+{ |
|
+ VALUE v = (VALUE)vstart; |
|
+ for (; v != (VALUE)vend; v += stride) { |
|
+ switch(BUILTIN_TYPE(v)) { |
|
+ case T_CLASS: |
|
+ case T_MODULE: |
|
+ case T_ICLASS: |
|
+ RCLASS(v)->cache->method_cache_version = 0; |
|
+ } |
|
+ } |
|
+ return 0; |
|
+} |
|
static void |
|
vm_clear_global_method_cache(void) |
|
{ |
|
- struct cache_entry *ent, *end; |
|
- |
|
- ent = cache; |
|
- end = ent + CACHE_SIZE; |
|
- while (ent < end) { |
|
- ent->filled_version = 0; |
|
- ent++; |
|
- } |
|
+ rb_objspace_each_objects(methods_cache_clear_callback, NULL); |
|
} |
|
|
|
void |
|
@@ -162,7 +157,7 @@ rb_method_entry_make(VALUE klass, ID mid, rb_method_type_t type, |
|
rb_method_definition_t *def, rb_method_flag_t noex) |
|
{ |
|
rb_method_entry_t *me; |
|
- st_table *mtbl; |
|
+ sa_table *mtbl; |
|
st_data_t data; |
|
|
|
if (NIL_P(klass)) { |
|
@@ -190,7 +185,7 @@ rb_method_entry_make(VALUE klass, ID mid, rb_method_type_t type, |
|
mtbl = RCLASS_M_TBL(klass); |
|
|
|
/* check re-definition */ |
|
- if (st_lookup(mtbl, mid, &data)) { |
|
+ if (sa_lookup(mtbl, (sa_index_t)mid, &data)) { |
|
rb_method_entry_t *old_me = (rb_method_entry_t *)data; |
|
rb_method_definition_t *old_def = old_me->def; |
|
|
|
@@ -248,7 +243,7 @@ rb_method_entry_make(VALUE klass, ID mid, rb_method_type_t type, |
|
} |
|
} |
|
|
|
- st_insert(mtbl, mid, (st_data_t) me); |
|
+ sa_insert(mtbl, (sa_index_t)mid, (st_data_t) me); |
|
|
|
return me; |
|
} |
|
@@ -371,7 +366,7 @@ search_method(VALUE klass, ID id) |
|
return 0; |
|
} |
|
|
|
- while (!st_lookup(RCLASS_M_TBL(klass), id, &body)) { |
|
+ while (!sa_lookup(RCLASS_M_TBL(klass), (sa_index_t)id, &body)) { |
|
klass = RCLASS_SUPER(klass); |
|
if (!klass) { |
|
return 0; |
|
@@ -393,20 +388,10 @@ rb_method_entry_get_without_cache(VALUE klass, ID id) |
|
rb_method_entry_t *me = search_method(klass, id); |
|
|
|
if (ruby_running) { |
|
- struct cache_entry *ent; |
|
- ent = cache + EXPR1(klass, id); |
|
- ent->filled_version = GET_VM_STATE_VERSION(); |
|
- ent->klass = klass; |
|
- |
|
if (UNDEFINED_METHOD_ENTRY_P(me)) { |
|
- ent->mid = id; |
|
- ent->me = 0; |
|
- me = 0; |
|
- } |
|
- else { |
|
- ent->mid = id; |
|
- ent->me = me; |
|
+ me = 0; |
|
} |
|
+ sa_insert(&RCLASS(klass)->cache->m_cache_tbl, (sa_index_t)id, (st_data_t)me); |
|
} |
|
|
|
return me; |
|
@@ -415,21 +400,23 @@ rb_method_entry_get_without_cache(VALUE klass, ID id) |
|
rb_method_entry_t * |
|
rb_method_entry(VALUE klass, ID id) |
|
{ |
|
- struct cache_entry *ent; |
|
+ rb_class_cache_t *cache = RCLASS(klass)->cache; |
|
+ rb_method_entry_t *me; |
|
|
|
- ent = cache + EXPR1(klass, id); |
|
- if (ent->filled_version == GET_VM_STATE_VERSION() && |
|
- ent->mid == id && ent->klass == klass) { |
|
- return ent->me; |
|
+ if (cache->method_cache_version != GET_VM_STATE_VERSION()) { |
|
+ sa_clear_no_free(&cache->m_cache_tbl); |
|
+ cache->method_cache_version = GET_VM_STATE_VERSION(); |
|
+ } |
|
+ else if (sa_lookup(&cache->m_cache_tbl, (sa_index_t)id, (st_data_t*)&me)) { |
|
+ return me; |
|
} |
|
- |
|
return rb_method_entry_get_without_cache(klass, id); |
|
} |
|
|
|
static void |
|
remove_method(VALUE klass, ID mid) |
|
{ |
|
- st_data_t key, data; |
|
+ st_data_t data; |
|
rb_method_entry_t *me = 0; |
|
|
|
if (klass == rb_cObject) { |
|
@@ -443,14 +430,13 @@ remove_method(VALUE klass, ID mid) |
|
rb_warn("removing `%s' may cause serious problems", rb_id2name(mid)); |
|
} |
|
|
|
- if (!st_lookup(RCLASS_M_TBL(klass), mid, &data) || |
|
+ if (!sa_lookup(RCLASS_M_TBL(klass), (sa_index_t)mid, &data) || |
|
!(me = (rb_method_entry_t *)data) || |
|
(!me->def || me->def->type == VM_METHOD_TYPE_UNDEF)) { |
|
rb_name_error(mid, "method `%s' not defined in %s", |
|
rb_id2name(mid), rb_class2name(klass)); |
|
} |
|
- key = (st_data_t)mid; |
|
- st_delete(RCLASS_M_TBL(klass), &key, &data); |
|
+ sa_delete(RCLASS_M_TBL(klass), (sa_index_t)mid, &data); |
|
|
|
rb_vm_check_redefinition_opt_method(me); |
|
rb_clear_cache_for_undef(klass, mid); |
I patched the latest 1.9.3 (p286) with the 7th patch (GC only). However, while running in passenger, the memory consumption is precisely the same as with the unpatched version. I verified that I'm really using the patched version, as the GC::Profiler.respond_to?(:raw_data) returns true. Is there some reason that something is still taints the objects in the memory?