diff --git a/src/class.c b/src/class.c index ba413dfb1..75682225b 100644 --- a/src/class.c +++ b/src/class.c @@ -16,6 +16,9 @@ #include #include #include + +#define MT_LOAD_FACTOR_NUM 3 // Numerator for load factor (represents 0.75) +#define MT_LOAD_FACTOR_DEN 4 // Denominator for load factor #include #include @@ -102,41 +105,55 @@ mt_put(mrb_state *mrb, mt_tbl *t, mrb_sym sym, mrb_sym flags, union mt_ptr ptr) { int pos, start, dpos = -1; - if (t->alloc == 0) { + // Rehash if t->alloc is 0 (initial allocation) + // or if (t->size + 1) / t->alloc would meet or exceed the load factor. + // This check helps maintain performance by keeping the table from getting too full. + if (t->alloc == 0 || ((t->size + 1) * MT_LOAD_FACTOR_DEN >= t->alloc * MT_LOAD_FACTOR_NUM)) { mt_rehash(mrb, t); } + // These assignments MUST come AFTER any potential mt_rehash call, + // as mt_rehash changes t->ptr and t->alloc. mrb_sym *keys = (mrb_sym*)&t->ptr[t->alloc]; union mt_ptr *vals = t->ptr; int hash = mrb_int_hash_func(mrb, sym); - start = pos = hash & (t->alloc-1); + start = pos = hash & (t->alloc-1); // t->alloc is always a power of 2 after mt_rehash + for (;;) { mrb_sym key = keys[pos]; - if (MT_KEY_SYM(key) == sym) { - value_set: + if (MT_KEY_SYM(key) == sym) { // Key found, update existing method + value_set: // Label to jump to for setting the value keys[pos] = MT_KEY(sym, flags); vals[pos] = ptr; return; } - else if (key == MT_EMPTY) { - t->size++; + else if (key == MT_EMPTY) { // Empty slot found + if (dpos != -1) { // If we passed a deleted slot, use that one instead + pos = dpos; + } + t->size++; // A new element is being added, so increment size. goto value_set; } - else if (key == MT_DELETED && dpos < 0) { - dpos = pos; + else if (key == MT_DELETED) { // Deleted slot found + if (dpos == -1) { // Store the first deleted slot found + dpos = pos; + } } - pos = (pos+1) & (t->alloc-1); - if (pos == start) { /* not found */ - if (dpos > 0) { - t->size++; + pos = (pos+1) & (t->alloc-1); // Move to the next slot (linear probing) + if (pos == start) { // Cycled through all slots + if (dpos != -1) { // If we found a deleted slot during the cycle, use it pos = dpos; + t->size++; // We are using a deleted slot for a new entry. goto value_set; } - /* no room */ + // No empty or deleted slot found in the probe sequence. The table is full. + // This rehash is a fallback. The load factor check should ideally prevent this. mt_rehash(mrb, t); - start = pos = hash & (t->alloc-1); + // Recalculate pointers and hash position after rehash keys = (mrb_sym*)&t->ptr[t->alloc]; vals = t->ptr; + start = pos = hash & (t->alloc-1); + dpos = -1; // Reset dpos as the table structure changed } } }