Object* LookupTable::store(STATE, Object* key, Object* val) { unsigned int num_entries, num_bins, bin; LookupTableBucket* entry; LookupTableBucket* last = NULL; num_entries = entries_->to_native(); num_bins = bins_->to_native(); if(max_density_p(num_entries, num_bins)) { redistribute(state, num_bins <<= 1); } key_to_sym(key); bin = find_bin(key_hash(key), num_bins); entry = try_as<LookupTableBucket>(values_->at(state, bin)); while(entry) { if(entry->key() == key) { entry->value(state, val); return val; } last = entry; entry = try_as<LookupTableBucket>(entry->next()); } if(last) { last->next(state, LookupTableBucket::create(state, key, val)); } else { values_->put(state, bin, LookupTableBucket::create(state, key, val)); } entries(state, Fixnum::from(num_entries + 1)); return val; }
Object* LookupTable::remove(STATE, Object* key, bool* removed) { hashval bin; LookupTableBucket* entry; LookupTableBucket* last = NULL; size_t num_entries = entries_->to_native(); size_t num_bins = bins_->to_native(); if(min_density_p(num_entries, num_bins) && (num_bins >> 1) >= LOOKUPTABLE_MIN_SIZE) { redistribute(state, num_bins >>= 1); } key_to_sym(key); bin = find_bin(key_hash(key), num_bins); entry = try_as<LookupTableBucket>(values_->at(state, bin)); while(entry) { if(entry->key() == key) { Object *val = entry->value(); if(last) { last->next(state, entry->next()); } else { values_->put(state, bin, entry->next()); } entries(state, Fixnum::from(entries_->to_native() - 1)); if(removed) *removed = true; return val; } last = entry; entry = try_as<LookupTableBucket>(entry->next()); } return Qnil; }
void LookupTable::redistribute(STATE, size_t size) { size_t num = bins_->to_native(); Tuple* new_values = Tuple::create(state, size); for(size_t i = 0; i < num; i++) { LookupTableBucket* entry = try_as<LookupTableBucket>(values_->at(state, i)); while(entry) { LookupTableBucket* link = try_as<LookupTableBucket>(entry->next()); entry->next(state, reinterpret_cast<LookupTableBucket *>(Qnil)); size_t bin = find_bin(key_hash(entry->key()), size); LookupTableBucket* slot = try_as<LookupTableBucket>(new_values->at(state, bin)); if(slot) { slot->append(state, entry); } else { new_values->put(state, bin, entry); } entry = link; } } values(state, new_values); bins(state, Fixnum::from(size)); }
Object* LookupTableBucket::append(STATE, LookupTableBucket* nxt) { LookupTableBucket* cur = try_as<LookupTableBucket>(this->next()); LookupTableBucket* last = this; while(cur) { last = cur; cur = try_as<LookupTableBucket>(cur->next()); } last->next(state, nxt); return nxt; }
void test_store_handles_entries_in_same_bin() { Object* k1 = Fixnum::from((4 << 4) | 15); Object* k2 = Fixnum::from((10 << 4) | 15); Object* k3 = Fixnum::from((11 << 4) | 15); Object* v1 = cTrue; Object* v2 = cFalse; Object* v3 = cTrue; tbl->store(state, k1, v1); tbl->store(state, k2, v2); tbl->store(state, k3, v3); TS_ASSERT_EQUALS(as<Integer>(tbl->entries())->to_native(), 3); LookupTableBucket* entry = tbl->find_entry(state, k1); TS_ASSERT(entry != cNil); TS_ASSERT(entry->next() != cNil); TS_ASSERT_EQUALS(entry->next()->key(), k2); entry = tbl->find_entry(state, k3); TS_ASSERT(entry != cNil); TS_ASSERT_EQUALS(entry->key(), k3); }
LookupTableBucket* LookupTable::find_entry(STATE, Object* key) { unsigned int bin; key_to_sym(key); bin = find_bin(key_hash(key), bins_->to_native()); LookupTableBucket *entry = try_as<LookupTableBucket>(values_->at(state, bin)); while(entry) { if(entry->key() == key) { return entry; } entry = try_as<LookupTableBucket>(entry->next()); } return reinterpret_cast<LookupTableBucket *>(Qnil); }
Array* LookupTable::collect(STATE, LookupTable* tbl, Object* (*action)(STATE, LookupTableBucket*)) { size_t i, j; Tuple* values; Array* ary = Array::create(state, tbl->entries()->to_native()); size_t num_bins = tbl->bins()->to_native(); values = tbl->values(); for(i = j = 0; i < num_bins; i++) { LookupTableBucket* entry = try_as<LookupTableBucket>(values->at(state, i)); while(entry) { ary->set(state, j++, action(state, entry)); entry = try_as<LookupTableBucket>(entry->next()); } } return ary; }
Array* LookupTable::collect(STATE, LookupTable* tbl, CollectAction& action) { size_t i, j; Tuple* values; LookupTableBucket* entry; Array* ary = Array::create(state, tbl->entries()->to_native()); size_t num_bins = tbl->bins()->to_native(); values = tbl->values(); for(i = j = 0; i < num_bins; i++) { entry = try_as<LookupTableBucket>(values->at(state, i)); while(entry) { Object* ret = action.call(state, entry); if(ret) ary->set(state, j++, ret); entry = try_as<LookupTableBucket>(entry->next()); } } return ary; }