From: Nobuyoshi Nakada Date: 2011-12-14T16:53:25+09:00 Subject: [ruby-dev:44998] [Feature] st_update() なかだです。 st_tableにある値を変更したり削除したりということがしたいことがま まあるのですが、st_lookup()とst_delete()/st_insert()の組み合わせ だとハッシュ値の計算や探索を二回繰り返さなくてはなりません。 そこで、以下のような関数はどうでしょうか。 int st_update(st_table *table, st_data_t key, int (*func)(st_data_t key, st_data_t *value, st_data_t arg), st_data_t arg); tableからkeyに対応するペアを探して、funcを呼び出します。funcが ST_DELETEを返した場合は相当するキーを削除、ST_CONTINUEを返した場 合は*valueで値を更新します。 diff --git i/include/ruby/st.h w/include/ruby/st.h index 50f2a75..ff3da2a 100644 --- i/include/ruby/st.h +++ w/include/ruby/st.h @@ -113,6 +113,7 @@ int st_insert(st_table *, st_data_t, st_data_t); 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_update(st_table *table, st_data_t key, int (*func)(st_data_t key, st_data_t *value, st_data_t arg), st_data_t arg); int st_foreach(st_table *, int (*)(ANYARGS), 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); diff --git i/st.c w/st.c index 0186667..d95f551 100644 --- i/st.c +++ w/st.c @@ -733,6 +733,62 @@ st_cleanup_safe(st_table *table, st_data_t never) } int +st_update(st_table *table, st_data_t key, int (*func)(st_data_t key, st_data_t *value, st_data_t arg), st_data_t arg) +{ + st_index_t hash_val, bin_pos; + register st_table_entry *ptr, **last, *tmp; + st_data_t value; + + 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) { + value = (st_data_t)table->bins[i*2+1]; + switch ((*func)(key, &value, arg)) { + case ST_CONTINUE: + table->bins[i*2+1] = (struct st_table_entry*)value; + break; + 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)); + } + return 1; + } + } + return 0; + } + + hash_val = do_hash(key, table); + FIND_ENTRY(table, ptr, hash_val, bin_pos); + + if (ptr == 0) { + return 0; + } + else { + value = ptr->record; + switch ((*func)(ptr->key, &value, arg)) { + case ST_CONTINUE: + ptr->record = value; + break; + case ST_DELETE: + last = &table->bins[bin_pos]; + for (; (tmp = *last) != 0; last = &tmp->next) { + if (ptr == tmp) { + tmp = ptr->fore; + *last = ptr->next; + REMOVE_ENTRY(table, ptr); + free(ptr); + break; + } + } + break; + } + return 1; + } +} + +int st_foreach(st_table *table, int (*func)(ANYARGS), st_data_t arg) { st_table_entry *ptr, **last, *tmp; -- --- 僕の前にBugはない。 --- 僕の後ろにBugはできる。 中田 伸悦