1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

st.c: use ccan linked-list

This improves the bm_vm2_bighash benchmark significantly by
removing branches during insert, but slows down anything
requiring iteration with the more complex loop termination
checking.

Speedup ratio of 1.10 - 1.20 is typical for the vm2_bighash
benchmark.

* include/ruby/st.h (struct st_table): hide struct list_head
* st.c (struct st_table_entry): adjust struct
  (head, tail): remove shortcut macros
  (st_head): new wrapper function
  (st_init_table_with_size): adjust to new struct and API
  (st_clear): ditto
  (add_direct): ditto
  (unpack_entries): ditto
  (rehash): ditto
  (st_copy): ditto
  (remove_entry): ditto
  (st_shift): ditto
  (st_foreach_check): ditto
  (st_foreach): ditto
  (get_keys): ditto
  (get_values): ditto
  (st_values_check): ditto
  (st_reverse_foreach_check): ditto (unused)
  (st_reverse_foreach): ditto (unused)
  [ruby-core:69726] [Misc #10278]

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@51034 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
normal 2015-06-25 19:01:25 +00:00
parent bbe6aa72fa
commit d8748874cc
3 changed files with 145 additions and 163 deletions

View file

@ -1,3 +1,26 @@
Fri Jun 26 03:52:27 2015 Eric Wong <e@80x24.org>
* include/ruby/st.h (struct st_table): hide struct list_head
* st.c (struct st_table_entry): adjust struct
(head, tail): remove shortcut macros
(st_head): new wrapper function
(st_init_table_with_size): adjust to new struct and API
(st_clear): ditto
(add_direct): ditto
(unpack_entries): ditto
(rehash): ditto
(st_copy): ditto
(remove_entry): ditto
(st_shift): ditto
(st_foreach_check): ditto
(st_foreach): ditto
(get_keys): ditto
(get_values): ditto
(st_values_check): ditto
(st_reverse_foreach_check): ditto (unused)
(st_reverse_foreach): ditto (unused)
[ruby-core:69726] [Misc #10278]
Thu Jun 25 21:24:28 2015 Naohisa Goto <ngotogenome@gmail.com> Thu Jun 25 21:24:28 2015 Naohisa Goto <ngotogenome@gmail.com>
* test/-ext-/popen_deadlock/test_popen_deadlock.rb: test [Bug #11265] * test/-ext-/popen_deadlock/test_popen_deadlock.rb: test [Bug #11265]

View file

@ -86,7 +86,7 @@ struct st_table {
union { union {
struct { struct {
struct st_table_entry **bins; struct st_table_entry **bins;
struct st_table_entry *head, *tail; void *private_list_head[2];
} big; } big;
struct { struct {
struct st_packed_entry *entries; struct st_packed_entry *entries;

203
st.c
View file

@ -14,6 +14,7 @@
#include <stdlib.h> #include <stdlib.h>
#endif #endif
#include <string.h> #include <string.h>
#include "ccan/list/list.h"
typedef struct st_table_entry st_table_entry; typedef struct st_table_entry st_table_entry;
@ -22,7 +23,7 @@ struct st_table_entry {
st_data_t key; st_data_t key;
st_data_t record; st_data_t record;
st_table_entry *next; st_table_entry *next;
st_table_entry *fore, *back; struct list_node olist;
}; };
typedef struct st_packed_entry { typedef struct st_packed_entry {
@ -107,8 +108,6 @@ st_realloc_bins(st_table_entry **bins, st_index_t newsize, st_index_t oldsize)
/* Shortcut */ /* Shortcut */
#define bins as.big.bins #define bins as.big.bins
#define head as.big.head
#define tail as.big.tail
#define real_entries as.packed.real_entries #define real_entries as.packed.real_entries
/* preparation for possible packing improvements */ /* preparation for possible packing improvements */
@ -194,6 +193,12 @@ stat_col(void)
} }
#endif #endif
static struct list_head *
st_head(const st_table *tbl)
{
return (struct list_head *)&tbl->as.big.private_list_head;
}
st_table* st_table*
st_init_table_with_size(const struct st_hash_type *type, st_index_t size) st_init_table_with_size(const struct st_hash_type *type, st_index_t size)
{ {
@ -219,14 +224,14 @@ st_init_table_with_size(const struct st_hash_type *type, st_index_t size)
tbl->entries_packed = size <= MAX_PACKED_HASH; tbl->entries_packed = size <= MAX_PACKED_HASH;
if (tbl->entries_packed) { if (tbl->entries_packed) {
size = ST_DEFAULT_PACKED_TABLE_SIZE; size = ST_DEFAULT_PACKED_TABLE_SIZE;
tbl->real_entries = 0;
} }
else { else {
size = new_size(size); /* round up to power-of-two */ size = new_size(size); /* round up to power-of-two */
list_head_init(st_head(tbl));
} }
tbl->num_bins = size; tbl->num_bins = size;
tbl->bins = st_alloc_bins(size); tbl->bins = st_alloc_bins(size);
tbl->head = 0;
tbl->tail = 0;
return tbl; return tbl;
} }
@ -277,7 +282,6 @@ void
st_clear(st_table *table) st_clear(st_table *table)
{ {
register st_table_entry *ptr, *next; register st_table_entry *ptr, *next;
st_index_t i;
if (table->entries_packed) { if (table->entries_packed) {
table->num_entries = 0; table->num_entries = 0;
@ -285,18 +289,13 @@ st_clear(st_table *table)
return; return;
} }
for (i = 0; i < table->num_bins; i++) { list_for_each_safe(st_head(table), ptr, next, olist) {
ptr = table->bins[i]; /* list_del is not needed */
table->bins[i] = 0;
while (ptr != 0) {
next = ptr->next;
st_free_entry(ptr); st_free_entry(ptr);
ptr = next;
}
} }
table->num_entries = 0; table->num_entries = 0;
table->head = 0; MEMZERO(table->bins, st_table_entry*, table->num_bins);
table->tail = 0; list_head_init(st_head(table));
} }
void void
@ -464,16 +463,7 @@ add_direct(st_table *table, st_data_t key, st_data_t value,
} }
entry = new_entry(table, key, value, hash_val, bin_pos); entry = new_entry(table, key, value, hash_val, bin_pos);
list_add_tail(st_head(table), &entry->olist);
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++; table->num_entries++;
} }
@ -482,7 +472,7 @@ unpack_entries(register st_table *table)
{ {
st_index_t i; st_index_t i;
st_packed_entry packed_bins[MAX_PACKED_HASH]; st_packed_entry packed_bins[MAX_PACKED_HASH];
register st_table_entry *entry, *preventry = 0, **chain; register st_table_entry *entry;
st_table tmp_table = *table; st_table tmp_table = *table;
MEMCPY(packed_bins, PACKED_BINS(table), st_packed_entry, MAX_PACKED_HASH); MEMCPY(packed_bins, PACKED_BINS(table), st_packed_entry, MAX_PACKED_HASH);
@ -494,22 +484,24 @@ unpack_entries(register st_table *table)
tmp_table.bins = st_realloc_bins(tmp_table.bins, ST_DEFAULT_INIT_TABLE_SIZE, tmp_table.num_bins); 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; tmp_table.num_bins = ST_DEFAULT_INIT_TABLE_SIZE;
#endif #endif
/*
* order is important here, we need to keep the original table
* walkable during GC (GC may be triggered by new_entry call)
*/
i = 0; i = 0;
chain = &tmp_table.head; list_head_init(st_head(&tmp_table));
do { do {
st_data_t key = packed_bins[i].key; st_data_t key = packed_bins[i].key;
st_data_t val = packed_bins[i].val; st_data_t val = packed_bins[i].val;
st_index_t hash = packed_bins[i].hash; st_index_t hash = packed_bins[i].hash;
entry = new_entry(&tmp_table, key, val, hash, entry = new_entry(&tmp_table, key, val, hash,
hash_pos(hash, ST_DEFAULT_INIT_TABLE_SIZE)); hash_pos(hash, ST_DEFAULT_INIT_TABLE_SIZE));
*chain = entry; list_add_tail(st_head(&tmp_table), &entry->olist);
entry->back = preventry;
preventry = entry;
chain = &entry->fore;
} while (++i < MAX_PACKED_HASH); } while (++i < MAX_PACKED_HASH);
*chain = NULL;
tmp_table.tail = entry;
*table = tmp_table; *table = tmp_table;
list_head_init(st_head(table));
list_append_list(st_head(table), st_head(&tmp_table));
} }
static void static void
@ -619,12 +611,10 @@ rehash(register st_table *table)
table->num_bins = new_num_bins; table->num_bins = new_num_bins;
table->bins = new_bins; table->bins = new_bins;
if ((ptr = table->head) != 0) { list_for_each(st_head(table), ptr, olist) {
do {
hash_val = hash_pos(ptr->hash, new_num_bins); hash_val = hash_pos(ptr->hash, new_num_bins);
ptr->next = new_bins[hash_val]; ptr->next = new_bins[hash_val];
new_bins[hash_val] = ptr; new_bins[hash_val] = ptr;
} while ((ptr = ptr->fore) != 0);
} }
} }
@ -632,9 +622,8 @@ st_table*
st_copy(st_table *old_table) st_copy(st_table *old_table)
{ {
st_table *new_table; st_table *new_table;
st_table_entry *ptr, *entry, *prev, **tailp; st_table_entry *ptr, *entry;
st_index_t num_bins = old_table->num_bins; st_index_t num_bins = old_table->num_bins;
st_index_t hash_val;
new_table = st_alloc_table(); new_table = st_alloc_table();
if (new_table == 0) { if (new_table == 0) {
@ -654,24 +643,12 @@ st_copy(st_table *old_table)
return new_table; return new_table;
} }
if ((ptr = old_table->head) != 0) { list_head_init(st_head(new_table));
prev = 0;
tailp = &new_table->head; list_for_each(st_head(old_table), ptr, olist) {
do { entry = new_entry(new_table, ptr->key, ptr->record, ptr->hash,
entry = st_alloc_entry(); hash_pos(ptr->hash, num_bins));
if (entry == 0) { list_add_tail(st_head(new_table), &entry->olist);
st_free_table(new_table);
return 0;
}
*entry = *ptr;
hash_val = hash_pos(entry->hash, num_bins);
entry->next = new_table->bins[hash_val];
new_table->bins[hash_val] = entry;
entry->back = prev;
*tailp = prev = entry;
tailp = &entry->fore;
} while ((ptr = ptr->fore) != 0);
new_table->tail = prev;
} }
return new_table; return new_table;
@ -680,17 +657,7 @@ st_copy(st_table *old_table)
static inline void static inline void
remove_entry(st_table *table, st_table_entry *ptr) remove_entry(st_table *table, st_table_entry *ptr)
{ {
if (ptr->fore == 0 && ptr->back == 0) { list_del(&ptr->olist);
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--; table->num_entries--;
} }
@ -770,6 +737,7 @@ st_delete_safe(register st_table *table, register st_data_t *key, st_data_t *val
int int
st_shift(register st_table *table, register st_data_t *key, st_data_t *value) st_shift(register st_table *table, register st_data_t *key, st_data_t *value)
{ {
st_table_entry *old;
st_table_entry **prev; st_table_entry **prev;
register st_table_entry *ptr; register st_table_entry *ptr;
@ -785,12 +753,13 @@ st_shift(register st_table *table, register st_data_t *key, st_data_t *value)
return 1; return 1;
} }
prev = &table->bins[hash_pos(table->head->hash, table->num_bins)]; old = list_pop(st_head(table), st_table_entry, olist);
while ((ptr = *prev) != table->head) prev = &ptr->next; table->num_entries--;
prev = &table->bins[hash_pos(old->hash, table->num_bins)];
while ((ptr = *prev) != old) prev = &ptr->next;
*prev = ptr->next; *prev = ptr->next;
if (value != 0) *value = ptr->record; if (value != 0) *value = ptr->record;
*key = ptr->key; *key = ptr->key;
remove_entry(table, ptr);
st_free_entry(ptr); st_free_entry(ptr);
return 1; return 1;
} }
@ -917,7 +886,8 @@ st_update(st_table *table, st_data_t key, st_update_callback_func *func, st_data
int int
st_foreach_check(st_table *table, int (*func)(ANYARGS), st_data_t arg, st_data_t never) st_foreach_check(st_table *table, int (*func)(ANYARGS), st_data_t arg, st_data_t never)
{ {
st_table_entry *ptr, **last, *tmp; st_table_entry *ptr, **last, *tmp, *next;
struct list_head *head;
enum st_retval retval; enum st_retval retval;
st_index_t i; st_index_t i;
@ -934,8 +904,10 @@ st_foreach_check(st_table *table, int (*func)(ANYARGS), st_data_t arg, st_data_t
FIND_ENTRY(table, ptr, hash, i); FIND_ENTRY(table, ptr, hash, i);
if (retval == ST_CHECK) { if (retval == ST_CHECK) {
if (!ptr) goto deleted; if (!ptr) goto deleted;
goto unpacked_continue;
} }
if (table->num_entries == 0) return 0;
head = st_head(table);
next = list_entry(ptr->olist.next, st_table_entry, olist);
goto unpacked; goto unpacked;
} }
switch (retval) { switch (retval) {
@ -960,14 +932,10 @@ st_foreach_check(st_table *table, int (*func)(ANYARGS), st_data_t arg, st_data_t
} }
return 0; return 0;
} }
else {
ptr = table->head;
}
if (ptr != 0) { head = st_head(table);
do { list_for_each_safe(head, ptr, next, olist) {
if (ptr->key == never) if (ptr->key != never) {
goto unpacked_continue;
i = hash_pos(ptr->hash, table->num_bins); i = hash_pos(ptr->hash, table->num_bins);
retval = (*func)(ptr->key, ptr->record, arg, 0); retval = (*func)(ptr->key, ptr->record, arg, 0);
unpacked: unpacked:
@ -983,8 +951,6 @@ st_foreach_check(st_table *table, int (*func)(ANYARGS), st_data_t arg, st_data_t
} }
/* fall through */ /* fall through */
case ST_CONTINUE: case ST_CONTINUE:
unpacked_continue:
ptr = ptr->fore;
break; break;
case ST_STOP: case ST_STOP:
return 0; return 0;
@ -992,16 +958,15 @@ st_foreach_check(st_table *table, int (*func)(ANYARGS), st_data_t arg, st_data_t
last = &table->bins[hash_pos(ptr->hash, table->num_bins)]; last = &table->bins[hash_pos(ptr->hash, table->num_bins)];
for (; (tmp = *last) != 0; last = &tmp->next) { for (; (tmp = *last) != 0; last = &tmp->next) {
if (ptr == tmp) { if (ptr == tmp) {
tmp = ptr->fore;
remove_entry(table, ptr); remove_entry(table, ptr);
ptr->key = ptr->record = never; ptr->key = ptr->record = never;
ptr->hash = 0; ptr->hash = 0;
ptr = tmp;
break; break;
} }
} }
if (table->num_entries == 0) return 0;
}
} }
} while (ptr && table->head);
} }
return 0; return 0;
} }
@ -1009,8 +974,9 @@ st_foreach_check(st_table *table, int (*func)(ANYARGS), st_data_t arg, st_data_t
int int
st_foreach(st_table *table, int (*func)(ANYARGS), st_data_t arg) st_foreach(st_table *table, int (*func)(ANYARGS), st_data_t arg)
{ {
st_table_entry *ptr, **last, *tmp; st_table_entry *ptr, **last, *tmp, *next;
enum st_retval retval; enum st_retval retval;
struct list_head *head;
st_index_t i; st_index_t i;
if (table->entries_packed) { if (table->entries_packed) {
@ -1024,6 +990,8 @@ st_foreach(st_table *table, int (*func)(ANYARGS), st_data_t arg)
if (!table->entries_packed) { if (!table->entries_packed) {
FIND_ENTRY(table, ptr, hash, i); FIND_ENTRY(table, ptr, hash, i);
if (!ptr) return 0; if (!ptr) return 0;
head = st_head(table);
next = list_entry(ptr->olist.next, st_table_entry, olist);
goto unpacked; goto unpacked;
} }
switch (retval) { switch (retval) {
@ -1040,18 +1008,14 @@ st_foreach(st_table *table, int (*func)(ANYARGS), st_data_t arg)
} }
return 0; return 0;
} }
else {
ptr = table->head;
}
if (ptr != 0) { head = st_head(table);
do { list_for_each_safe(head, ptr, next, olist) {
i = hash_pos(ptr->hash, table->num_bins); i = hash_pos(ptr->hash, table->num_bins);
retval = (*func)(ptr->key, ptr->record, arg, 0); retval = (*func)(ptr->key, ptr->record, arg, 0);
unpacked: unpacked:
switch (retval) { switch (retval) {
case ST_CONTINUE: case ST_CONTINUE:
ptr = ptr->fore;
break; break;
case ST_CHECK: case ST_CHECK:
case ST_STOP: case ST_STOP:
@ -1060,16 +1024,14 @@ st_foreach(st_table *table, int (*func)(ANYARGS), st_data_t arg)
last = &table->bins[hash_pos(ptr->hash, table->num_bins)]; last = &table->bins[hash_pos(ptr->hash, table->num_bins)];
for (; (tmp = *last) != 0; last = &tmp->next) { for (; (tmp = *last) != 0; last = &tmp->next) {
if (ptr == tmp) { if (ptr == tmp) {
tmp = ptr->fore;
*last = ptr->next; *last = ptr->next;
remove_entry(table, ptr); remove_entry(table, ptr);
st_free_entry(ptr); st_free_entry(ptr);
ptr = tmp;
break; break;
} }
} }
if (table->num_entries == 0) return 0;
} }
} while (ptr && table->head);
} }
return 0; return 0;
} }
@ -1091,9 +1053,11 @@ get_keys(st_table *table, st_data_t *keys, st_index_t size, int check, st_data_t
} }
} }
else { else {
st_table_entry *ptr = table->head; st_table_entry *ptr;
st_data_t *keys_end = keys + size; st_data_t *keys_end = keys + size;
for (; ptr && keys < keys_end; ptr = ptr->fore) {
list_for_each(st_head(table), ptr, olist) {
if (keys >= keys_end) break;
key = ptr->key; key = ptr->key;
if (check && key == never) continue; if (check && key == never) continue;
*keys++ = key; *keys++ = key;
@ -1132,9 +1096,11 @@ get_values(st_table *table, st_data_t *values, st_index_t size, int check, st_da
} }
} }
else { else {
st_table_entry *ptr = table->head; st_table_entry *ptr;
st_data_t *values_end = values + size; st_data_t *values_end = values + size;
for (; ptr && values < values_end; ptr = ptr->fore) {
list_for_each(st_head(table), ptr, olist) {
if (values >= values_end) break;
key = ptr->key; key = ptr->key;
if (check && key == never) continue; if (check && key == never) continue;
*values++ = ptr->record; *values++ = ptr->record;
@ -1160,7 +1126,8 @@ st_values_check(st_table *table, st_data_t *values, st_index_t size, st_data_t n
int int
st_reverse_foreach_check(st_table *table, int (*func)(ANYARGS), st_data_t arg, st_data_t never) st_reverse_foreach_check(st_table *table, int (*func)(ANYARGS), st_data_t arg, st_data_t never)
{ {
st_table_entry *ptr, **last, *tmp; st_table_entry *ptr, **last, *tmp, *next;
struct list_head *head;
enum st_retval retval; enum st_retval retval;
st_index_t i; st_index_t i;
@ -1178,8 +1145,10 @@ st_reverse_foreach_check(st_table *table, int (*func)(ANYARGS), st_data_t arg, s
FIND_ENTRY(table, ptr, hash, i); FIND_ENTRY(table, ptr, hash, i);
if (retval == ST_CHECK) { if (retval == ST_CHECK) {
if (!ptr) goto deleted; if (!ptr) goto deleted;
goto unpacked_continue;
} }
if (table->num_entries == 0) return 0;
head = st_head(table);
next = list_entry(ptr->olist.next, st_table_entry, olist);
goto unpacked; goto unpacked;
} }
switch (retval) { switch (retval) {
@ -1204,14 +1173,10 @@ st_reverse_foreach_check(st_table *table, int (*func)(ANYARGS), st_data_t arg, s
} }
return 0; return 0;
} }
else {
ptr = table->tail;
}
if (ptr != 0) { head = st_head(table);
do { list_for_each_rev_safe(head, ptr, next, olist) {
if (ptr->key == never) if (ptr->key != never) {
goto unpacked_continue;
i = hash_pos(ptr->hash, table->num_bins); i = hash_pos(ptr->hash, table->num_bins);
retval = (*func)(ptr->key, ptr->record, arg, 0); retval = (*func)(ptr->key, ptr->record, arg, 0);
unpacked: unpacked:
@ -1227,8 +1192,6 @@ st_reverse_foreach_check(st_table *table, int (*func)(ANYARGS), st_data_t arg, s
} }
/* fall through */ /* fall through */
case ST_CONTINUE: case ST_CONTINUE:
unpacked_continue:
ptr = ptr->back;
break; break;
case ST_STOP: case ST_STOP:
return 0; return 0;
@ -1236,16 +1199,15 @@ st_reverse_foreach_check(st_table *table, int (*func)(ANYARGS), st_data_t arg, s
last = &table->bins[hash_pos(ptr->hash, table->num_bins)]; last = &table->bins[hash_pos(ptr->hash, table->num_bins)];
for (; (tmp = *last) != 0; last = &tmp->next) { for (; (tmp = *last) != 0; last = &tmp->next) {
if (ptr == tmp) { if (ptr == tmp) {
tmp = ptr->back;
remove_entry(table, ptr); remove_entry(table, ptr);
ptr->key = ptr->record = never; ptr->key = ptr->record = never;
ptr->hash = 0; ptr->hash = 0;
ptr = tmp;
break; break;
} }
} }
if (table->num_entries == 0) return 0;
}
} }
} while (ptr && table->head);
} }
return 0; return 0;
} }
@ -1253,8 +1215,9 @@ st_reverse_foreach_check(st_table *table, int (*func)(ANYARGS), st_data_t arg, s
int int
st_reverse_foreach(st_table *table, int (*func)(ANYARGS), st_data_t arg) st_reverse_foreach(st_table *table, int (*func)(ANYARGS), st_data_t arg)
{ {
st_table_entry *ptr, **last, *tmp; st_table_entry *ptr, **last, *tmp, *next;
enum st_retval retval; enum st_retval retval;
struct list_head *head;
st_index_t i; st_index_t i;
if (table->entries_packed) { if (table->entries_packed) {
@ -1269,6 +1232,8 @@ st_reverse_foreach(st_table *table, int (*func)(ANYARGS), st_data_t arg)
if (!table->entries_packed) { if (!table->entries_packed) {
FIND_ENTRY(table, ptr, hash, i); FIND_ENTRY(table, ptr, hash, i);
if (!ptr) return 0; if (!ptr) return 0;
head = st_head(table);
next = list_entry(ptr->olist.next, st_table_entry, olist);
goto unpacked; goto unpacked;
} }
switch (retval) { switch (retval) {
@ -1284,18 +1249,14 @@ st_reverse_foreach(st_table *table, int (*func)(ANYARGS), st_data_t arg)
} }
return 0; return 0;
} }
else {
ptr = table->tail;
}
if (ptr != 0) { head = st_head(table);
do { list_for_each_rev_safe(head, ptr, next, olist) {
i = hash_pos(ptr->hash, table->num_bins); i = hash_pos(ptr->hash, table->num_bins);
retval = (*func)(ptr->key, ptr->record, arg, 0); retval = (*func)(ptr->key, ptr->record, arg, 0);
unpacked: unpacked:
switch (retval) { switch (retval) {
case ST_CONTINUE: case ST_CONTINUE:
ptr = ptr->back;
break; break;
case ST_CHECK: case ST_CHECK:
case ST_STOP: case ST_STOP:
@ -1304,16 +1265,14 @@ st_reverse_foreach(st_table *table, int (*func)(ANYARGS), st_data_t arg)
last = &table->bins[hash_pos(ptr->hash, table->num_bins)]; last = &table->bins[hash_pos(ptr->hash, table->num_bins)];
for (; (tmp = *last) != 0; last = &tmp->next) { for (; (tmp = *last) != 0; last = &tmp->next) {
if (ptr == tmp) { if (ptr == tmp) {
tmp = ptr->back;
*last = ptr->next; *last = ptr->next;
remove_entry(table, ptr); remove_entry(table, ptr);
st_free_entry(ptr); st_free_entry(ptr);
ptr = tmp;
break; break;
} }
} }
if (table->num_entries == 0) return 0;
} }
} while (ptr && table->head);
} }
return 0; return 0;
} }