Skip to content

Commit

Permalink
[Bug #19969] Rehash after deleted a half or more
Browse files Browse the repository at this point in the history
  • Loading branch information
nobu committed Nov 9, 2023
1 parent c49adfa commit d73c1a3
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 11 deletions.
18 changes: 18 additions & 0 deletions hash.c
Original file line number Diff line number Diff line change
Expand Up @@ -1423,6 +1423,16 @@ rb_hash_foreach(VALUE hash, rb_foreach_func *func, VALUE farg)
hash_verify(hash);
}

void rb_st_compact_table(st_table *tab);

static void
rehash_after_delete(VALUE hash)
{
if (RHASH_ST_TABLE_P(hash)) {
rb_st_compact_table(RHASH_ST_TABLE(hash));
}
}

static VALUE
hash_alloc_flags(VALUE klass, VALUE flags, VALUE ifnone, bool st)
{
Expand Down Expand Up @@ -2397,6 +2407,7 @@ rb_hash_delete_m(VALUE hash, VALUE key)
val = rb_hash_delete_entry(hash, key);

if (!UNDEF_P(val)) {
rehash_after_delete(hash);
return val;
}
else {
Expand Down Expand Up @@ -2517,6 +2528,7 @@ rb_hash_delete_if(VALUE hash)
rb_hash_modify_check(hash);
if (!RHASH_TABLE_EMPTY_P(hash)) {
rb_hash_foreach(hash, delete_if_i, hash);
rehash_after_delete(hash);
}
return hash;
}
Expand Down Expand Up @@ -2580,6 +2592,7 @@ rb_hash_reject(VALUE hash)
result = hash_dup_with_compare_by_id(hash);
if (!RHASH_EMPTY_P(hash)) {
rb_hash_foreach(result, delete_if_i, result);
rehash_after_delete(result);
}
return result;
}
Expand Down Expand Up @@ -2639,6 +2652,7 @@ rb_hash_except(int argc, VALUE *argv, VALUE hash)
key = argv[i];
rb_hash_delete(result, key);
}
rehash_after_delete(result);

return result;
}
Expand Down Expand Up @@ -2734,6 +2748,7 @@ rb_hash_select(VALUE hash)
result = hash_dup_with_compare_by_id(hash);
if (!RHASH_EMPTY_P(hash)) {
rb_hash_foreach(result, keep_if_i, result);
rehash_after_delete(result);
}
return result;
}
Expand Down Expand Up @@ -3253,6 +3268,7 @@ rb_hash_transform_keys_bang(int argc, VALUE *argv, VALUE hash)
rb_ary_clear(pairs);
rb_hash_clear(new_keys);
}
rehash_after_delete(hash);
return hash;
}

Expand Down Expand Up @@ -3303,6 +3319,7 @@ rb_hash_transform_values(VALUE hash)

if (!RHASH_EMPTY_P(hash)) {
rb_hash_stlike_foreach_with_replace(result, transform_values_foreach_func, transform_values_foreach_replace, result);
rehash_after_delete(result);
}

return result;
Expand Down Expand Up @@ -4267,6 +4284,7 @@ rb_hash_compact(VALUE hash)
VALUE result = rb_hash_dup(hash);
if (!RHASH_EMPTY_P(hash)) {
rb_hash_foreach(result, delete_if_nil, result);
rehash_after_delete(result);
}
else if (rb_hash_compare_by_id_p(hash)) {
result = rb_hash_compare_by_id(result);
Expand Down
8 changes: 8 additions & 0 deletions include/ruby/st.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,14 @@ struct st_table {

enum st_retval {ST_CONTINUE, ST_STOP, ST_DELETE, ST_CHECK, ST_REPLACE};

/* Return the number of allocated entries of table TAB. */
static inline st_index_t
rb_st_allocated_entries(const st_table *tab)
{
return ((st_index_t) 1)<<tab->entry_power;
}
#define st_get_allocated_entries(tab) rb_st_allocated_entries(tab)

size_t rb_st_table_size(const struct st_table *tbl);
#define st_table_size rb_st_table_size
st_table *rb_st_init_table(const struct st_hash_type *);
Expand Down
40 changes: 29 additions & 11 deletions st.c
Original file line number Diff line number Diff line change
Expand Up @@ -717,39 +717,46 @@ count_collision(const struct st_hash_type *type)
#error "REBUILD_THRESHOLD should be >= 2"
#endif

static void rebuild_table_with(st_table *new_tab, st_table *tab);

/* Rebuild table TAB. Rebuilding removes all deleted bins and entries
and can change size of the table entries and bins arrays.
Rebuilding is implemented by creation of a new table or by
compaction of the existing one. */
static void
rebuild_table(st_table *tab)
{
st_index_t i, ni;
unsigned int size_ind;
st_table *new_tab;
st_table_entry *new_entries;
st_table_entry *curr_entry_ptr;
st_index_t *bins;
st_index_t bin_ind;

if ((2 * tab->num_entries <= get_allocated_entries(tab)
&& REBUILD_THRESHOLD * tab->num_entries > get_allocated_entries(tab))
|| tab->num_entries < (1 << MINIMAL_POWER2)) {
/* Compaction: */
tab->num_entries = 0;
if (tab->bins != NULL)
initialize_bins(tab);
new_tab = tab;
new_entries = tab->entries;
rebuild_table_with(tab, tab);
}
else {
st_table *new_tab;
/* This allocation could trigger GC and compaction. If tab is the
* gen_iv_tbl, then tab could have changed in size due to objects being
* freed and/or moved. Do not store attributes of tab before this line. */
new_tab = st_init_table_with_size(tab->type,
2 * tab->num_entries - 1);
new_entries = new_tab->entries;
rebuild_table_with(new_tab, tab);
}
}

static void
rebuild_table_with(st_table *new_tab, st_table *tab)
{
st_index_t i, ni;
unsigned int size_ind;
st_table_entry *new_entries;
st_table_entry *curr_entry_ptr;
st_index_t *bins;
st_index_t bin_ind;

new_entries = new_tab->entries;

ni = 0;
bins = new_tab->bins;
Expand Down Expand Up @@ -2296,4 +2303,15 @@ rb_st_nth_key(st_table *tab, st_index_t index)
}
}

void
rb_st_compact_table(st_table *tab)
{
if (REBUILD_THRESHOLD * tab->num_entries <= get_allocated_entries(tab)) {
/* Compaction: */
st_table *new_tab = st_init_table_with_size(tab->type,
tab->num_entries / 2);
rebuild_table_with(new_tab, tab);
}
}

#endif

0 comments on commit d73c1a3

Please sign in to comment.