Feature #22011 » changes.patch
| st.c | ||
|---|---|---|
|
#define ATTRIBUTE_UNUSED
|
||
|
#endif
|
||
|
/*
|
||
|
* The parser embeds this file through parser_st.c. Keep the new layout scoped to
|
||
|
* the main VM build unless a caller explicitly overrides the switch.
|
||
|
*/
|
||
|
#ifndef ST_USE_SWISS_BINS
|
||
|
# ifdef RUBY_EXPORT
|
||
|
# define ST_USE_SWISS_BINS 1
|
||
|
# else
|
||
|
# define ST_USE_SWISS_BINS 0
|
||
|
# endif
|
||
|
#endif
|
||
|
/* The type of hashes. */
|
||
|
typedef st_index_t st_hash_t;
|
||
|
struct st_table_entry {
|
||
|
#if !ST_USE_SWISS_BINS
|
||
|
st_hash_t hash;
|
||
|
#endif
|
||
|
st_data_t key;
|
||
|
st_data_t record;
|
||
|
};
|
||
| ... | ... | |
|
#endif
|
||
|
#define EQUAL(tab,x,y) ((x) == (y) || (*(tab)->type->compare)((x),(y)) == 0)
|
||
|
#if ST_USE_SWISS_BINS
|
||
|
typedef uint32_t st_stored_hash_t;
|
||
|
#endif
|
||
|
static inline st_index_t get_allocated_entries(const st_table *tab);
|
||
|
static inline size_t st_entries_memsize(const st_table *tab);
|
||
|
static inline size_t st_table_alloc_memsize(const st_table *tab);
|
||
|
#if ST_USE_SWISS_BINS
|
||
|
#define ST_RESERVED_HASH_VAL ((st_hash_t)UINT32_MAX)
|
||
|
#define ST_RESERVED_HASH_SUBSTITUTION_VAL ((st_hash_t)0)
|
||
|
static inline st_index_t
|
||
|
entry_index_from_ptr(const st_table *tab, const st_table_entry *entry)
|
||
|
{
|
||
|
return (st_index_t)(entry - tab->entries);
|
||
|
}
|
||
|
static inline st_stored_hash_t *
|
||
|
st_hashes_ptr(const st_table *tab)
|
||
|
{
|
||
|
return (st_stored_hash_t *)((char *)tab->entries
|
||
|
+ get_allocated_entries(tab) * sizeof(st_table_entry));
|
||
|
}
|
||
|
#define ST_HASH_AT_IDX(tab, ind) ((st_hash_t)st_hashes_ptr(tab)[ind])
|
||
|
#define ST_HASH_AT_PTR(tab, ptr) ST_HASH_AT_IDX((tab), entry_index_from_ptr((tab), (ptr)))
|
||
|
#define ST_SET_HASH_AT_IDX(tab, ind, hash_val) \
|
||
|
(st_hashes_ptr(tab)[ind] = (st_stored_hash_t)(hash_val))
|
||
|
#define ST_SET_HASH_AT_PTR(tab, ptr, hash_val) \
|
||
|
ST_SET_HASH_AT_IDX((tab), entry_index_from_ptr((tab), (ptr)), (hash_val))
|
||
|
#else
|
||
|
#define ST_RESERVED_HASH_VAL RESERVED_HASH_VAL
|
||
|
#define ST_RESERVED_HASH_SUBSTITUTION_VAL RESERVED_HASH_SUBSTITUTION_VAL
|
||
|
#define ST_HASH_AT_IDX(tab, ind) ((tab)->entries[ind].hash)
|
||
|
#define ST_HASH_AT_PTR(tab, ptr) ((ptr)->hash)
|
||
|
#define ST_SET_HASH_AT_IDX(tab, ind, hash_val) ((tab)->entries[ind].hash = (hash_val))
|
||
|
#define ST_SET_HASH_AT_PTR(tab, ptr, hash_val) ((ptr)->hash = (hash_val))
|
||
|
#endif
|
||
|
#define PTR_EQUAL(tab, ptr, hash_val, key_) \
|
||
|
(ST_HASH_AT_PTR((tab), (ptr)) == (hash_val) && EQUAL((tab), (key_), (ptr)->key))
|
||
|
#define SET_PTR_EQUAL(tab, ptr, hash_val, key_) \
|
||
|
((ptr)->hash == (hash_val) && EQUAL((tab), (key_), (ptr)->key))
|
||
|
/* As PTR_EQUAL only its result is returned in RES. REBUILT_P is set
|
||
|
up to TRUE if the table is rebuilt during the comparison. */
|
||
|
#define DO_PTR_EQUAL_CHECK(tab, ptr, hash_val, key, res, rebuilt_p) \
|
||
|
do { \
|
||
|
unsigned int _old_rebuilds_num = (tab)->rebuilds_num; \
|
||
|
res = PTR_EQUAL(tab, ptr, hash_val, key); \
|
||
|
rebuilt_p = _old_rebuilds_num != (tab)->rebuilds_num; \
|
||
|
unsigned int _old_rebuilds_num = (tab)->rebuilds_num; \
|
||
|
res = PTR_EQUAL(tab, ptr, hash_val, key); \
|
||
|
rebuilt_p = _old_rebuilds_num != (tab)->rebuilds_num; \
|
||
|
} while (FALSE)
|
||
|
#define SET_DO_PTR_EQUAL_CHECK(tab, ptr, hash_val, key, res, rebuilt_p) \
|
||
|
do { \
|
||
|
unsigned int _old_rebuilds_num = (tab)->rebuilds_num; \
|
||
|
res = SET_PTR_EQUAL(tab, ptr, hash_val, key); \
|
||
|
rebuilt_p = _old_rebuilds_num != (tab)->rebuilds_num; \
|
||
|
} while (FALSE)
|
||
|
/* Features of a table. */
|
||
| ... | ... | |
|
return hash == RESERVED_HASH_VAL ? RESERVED_HASH_SUBSTITUTION_VAL : hash;
|
||
|
}
|
||
|
static inline st_hash_t
|
||
|
st_normalize_hash_value(st_hash_t hash)
|
||
|
{
|
||
|
#if ST_USE_SWISS_BINS
|
||
|
hash = (st_hash_t)(st_stored_hash_t)hash;
|
||
|
#endif
|
||
|
return hash == ST_RESERVED_HASH_VAL ? ST_RESERVED_HASH_SUBSTITUTION_VAL : hash;
|
||
|
}
|
||
|
/* Return hash value of KEY for table TAB. */
|
||
|
static inline st_hash_t
|
||
|
do_hash(st_data_t key, st_table *tab)
|
||
|
{
|
||
|
st_hash_t hash = (st_hash_t)(tab->type->hash)(key);
|
||
|
return normalize_hash_value(hash);
|
||
|
return st_normalize_hash_value(hash);
|
||
|
}
|
||
|
/* Power of 2 defining the minimal number of allocated entries. */
|
||
| ... | ... | |
|
/* Mark I-th bin of table TAB as empty, in other words not
|
||
|
corresponding to any entry. */
|
||
|
#if ST_USE_SWISS_BINS
|
||
|
#define MARK_BIN_EMPTY(tab, i) \
|
||
|
do { \
|
||
|
st_swiss_set_bin((tab), i, EMPTY_BIN); \
|
||
|
st_swiss_set_ctrl((tab), i, ST_CTRL_EMPTY); \
|
||
|
} while (0)
|
||
|
#else
|
||
|
#define MARK_BIN_EMPTY(tab, i) (set_bin((tab)->bins, get_size_ind(tab), i, EMPTY_BIN))
|
||
|
#endif
|
||
|
/* Values used for not found entry and bin with given
|
||
|
characteristics. */
|
||
| ... | ... | |
|
/* Mark I-th bin of table TAB as corresponding to a deleted table
|
||
|
entry. Update number of entries in the table and number of bins
|
||
|
corresponding to deleted entries. */
|
||
|
#if ST_USE_SWISS_BINS
|
||
|
#define MARK_BIN_DELETED(tab, i) \
|
||
|
do { \
|
||
|
st_swiss_set_bin((tab), i, DELETED_BIN); \
|
||
|
st_swiss_set_ctrl((tab), i, ST_CTRL_DELETED); \
|
||
|
} while (0)
|
||
|
#else
|
||
|
#define MARK_BIN_DELETED(tab, i) \
|
||
|
do { \
|
||
|
set_bin((tab)->bins, get_size_ind(tab), i, DELETED_BIN); \
|
||
|
} while (0)
|
||
|
#endif
|
||
|
/* Macros to check that value B is used empty bins and bins
|
||
|
corresponding deleted entries. */
|
||
| ... | ... | |
|
/* Macros to check empty bins and bins corresponding to deleted
|
||
|
entries. Bins are given by their index I in table TAB. */
|
||
|
#if ST_USE_SWISS_BINS
|
||
|
#define IND_EMPTY_BIN_P(tab, i) (EMPTY_BIN_P(st_swiss_get_bin((tab), i)))
|
||
|
#define IND_DELETED_BIN_P(tab, i) (DELETED_BIN_P(st_swiss_get_bin((tab), i)))
|
||
|
#define IND_EMPTY_OR_DELETED_BIN_P(tab, i) (EMPTY_OR_DELETED_BIN_P(st_swiss_get_bin((tab), i)))
|
||
|
#else
|
||
|
#define IND_EMPTY_BIN_P(tab, i) (EMPTY_BIN_P(get_bin((tab)->bins, get_size_ind(tab), i)))
|
||
|
#define IND_DELETED_BIN_P(tab, i) (DELETED_BIN_P(get_bin((tab)->bins, get_size_ind(tab), i)))
|
||
|
#define IND_EMPTY_OR_DELETED_BIN_P(tab, i) (EMPTY_OR_DELETED_BIN_P(get_bin((tab)->bins, get_size_ind(tab), i)))
|
||
|
#endif
|
||
|
/* Macros for marking and checking deleted entries given by their
|
||
|
pointer E_PTR. */
|
||
|
#define MARK_ENTRY_DELETED(e_ptr) ((e_ptr)->hash = RESERVED_HASH_VAL)
|
||
|
#define DELETED_ENTRY_P(e_ptr) ((e_ptr)->hash == RESERVED_HASH_VAL)
|
||
|
/* Macros for marking and checking deleted entries given by their pointer. */
|
||
|
#define MARK_ENTRY_DELETED(tab, e_ptr) ST_SET_HASH_AT_PTR((tab), (e_ptr), ST_RESERVED_HASH_VAL)
|
||
|
#define DELETED_ENTRY_P(tab, e_ptr) (ST_HASH_AT_PTR((tab), (e_ptr)) == ST_RESERVED_HASH_VAL)
|
||
|
#define SET_MARK_ENTRY_DELETED(e_ptr) ((e_ptr)->hash = RESERVED_HASH_VAL)
|
||
|
#define SET_DELETED_ENTRY_P(e_ptr) ((e_ptr)->hash == RESERVED_HASH_VAL)
|
||
|
/* Return bin size index of table TAB. */
|
||
|
static inline unsigned int
|
||
| ... | ... | |
|
return ((st_index_t) 1)<<tab->entry_power;
|
||
|
}
|
||
|
#if ST_USE_SWISS_BINS
|
||
|
static inline bool
|
||
|
st_packs_bins(const st_table *tab)
|
||
|
{
|
||
|
return tab->entry_power > MAX_POWER2_FOR_TABLES_WITHOUT_BINS;
|
||
|
}
|
||
|
#endif
|
||
|
#if !ST_USE_SWISS_BINS
|
||
|
/* Return size of the allocated bins of table TAB. */
|
||
|
static inline st_index_t
|
||
|
bins_size(const st_table *tab)
|
||
|
{
|
||
|
return features[tab->entry_power].bins_words * sizeof (st_index_t);
|
||
|
}
|
||
|
#endif
|
||
|
#if ST_USE_SWISS_BINS
|
||
|
#define ST_SWISS_GROUP_SIZE 8
|
||
|
#define ST_SWISS_MAX_LOAD_NUM 7
|
||
|
#define ST_SWISS_MAX_LOAD_DEN 8
|
||
|
#define ST_CTRL_EMPTY 0xff
|
||
|
#define ST_CTRL_DELETED 0xfe
|
||
|
static inline st_index_t
|
||
|
st_swiss_groups_num(const st_table *tab)
|
||
|
{
|
||
|
return get_bins_num(tab) / ST_SWISS_GROUP_SIZE;
|
||
|
}
|
||
|
static inline st_index_t
|
||
|
st_swiss_rebuild_bound(const st_table *tab)
|
||
|
{
|
||
|
return get_bins_num(tab) / ST_SWISS_MAX_LOAD_DEN * ST_SWISS_MAX_LOAD_NUM;
|
||
|
}
|
||
|
static inline st_index_t
|
||
|
st_swiss_size_for_entries(st_index_t size)
|
||
|
{
|
||
|
return size == 0 ? 0 : size + (size + ST_SWISS_MAX_LOAD_NUM - 1) / ST_SWISS_MAX_LOAD_NUM - 1;
|
||
|
}
|
||
|
static inline st_index_t
|
||
|
st_swiss_group_ind(st_index_t ind)
|
||
|
{
|
||
|
return ind / ST_SWISS_GROUP_SIZE;
|
||
|
}
|
||
|
static inline unsigned int
|
||
|
st_swiss_group_offset(st_index_t ind)
|
||
|
{
|
||
|
return (unsigned int)(ind & (ST_SWISS_GROUP_SIZE - 1));
|
||
|
}
|
||
|
static inline unsigned int
|
||
|
st_swiss_ctrl_shift(st_index_t ind)
|
||
|
{
|
||
|
return st_swiss_group_offset(ind) * CHAR_BIT;
|
||
|
}
|
||
|
static inline st_index_t
|
||
|
st_swiss_get_bin(const st_table *tab, st_index_t ind)
|
||
|
{
|
||
|
return tab->bins[ind];
|
||
|
}
|
||
|
static inline void
|
||
|
st_swiss_set_bin(st_table *tab, st_index_t ind, st_index_t bin)
|
||
|
{
|
||
|
tab->bins[ind] = bin;
|
||
|
}
|
||
|
static inline uint64_t *
|
||
|
st_swiss_ctrl_groups(const st_table *tab)
|
||
|
{
|
||
|
return (uint64_t *)((char *)tab->bins + get_bins_num(tab) * sizeof(st_index_t));
|
||
|
}
|
||
|
static inline void
|
||
|
st_swiss_set_ctrl(st_table *tab, st_index_t ind, unsigned char ctrl)
|
||
|
{
|
||
|
uint64_t *group = &st_swiss_ctrl_groups(tab)[st_swiss_group_ind(ind)];
|
||
|
unsigned int shift = st_swiss_ctrl_shift(ind);
|
||
|
uint64_t mask = UINT64_C(0xff) << shift;
|
||
|
*group = (*group & ~mask) | ((uint64_t)ctrl << shift);
|
||
|
}
|
||
|
static inline st_index_t
|
||
|
st_bins_alloc_size(const st_table *tab)
|
||
|
{
|
||
|
return get_bins_num(tab) * sizeof(st_index_t)
|
||
|
+ st_swiss_groups_num(tab) * sizeof(uint64_t);
|
||
|
}
|
||
|
static inline st_index_t *
|
||
|
st_packed_bins_ptr(const st_table *tab)
|
||
|
{
|
||
|
if (st_packs_bins(tab)) {
|
||
|
return (st_index_t *)((char *)tab->entries + st_entries_memsize(tab));
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
static inline void
|
||
|
st_enable_packed_bins(st_table *tab)
|
||
|
{
|
||
|
tab->bins = st_packed_bins_ptr(tab);
|
||
|
}
|
||
|
static inline unsigned char
|
||
|
st_swiss_h2(st_hash_t hash)
|
||
|
{
|
||
|
/* Mix high bits into the low 7 bits: ID-like hashes have poor top bits. */
|
||
|
return (unsigned char)((hash ^ (hash >> 16)) & 0x7f);
|
||
|
}
|
||
|
static inline void
|
||
|
st_swiss_set_entry_bin(st_table *tab, st_index_t ind, st_index_t entry_ind, st_hash_t hash)
|
||
|
{
|
||
|
st_swiss_set_bin(tab, ind, entry_ind + ENTRY_BASE);
|
||
|
st_swiss_set_ctrl(tab, ind, st_swiss_h2(hash));
|
||
|
}
|
||
|
#define ST_GET_TABLE_BIN(tab, ind) st_swiss_get_bin((tab), (ind))
|
||
|
#define ST_SET_TABLE_BIN(tab, ind, bin) st_swiss_set_bin((tab), (ind), (bin))
|
||
|
#define ST_SET_TABLE_ENTRY_BIN(tab, ind, entry_ind, hash) st_swiss_set_entry_bin((tab), (ind), (entry_ind), (hash))
|
||
|
static inline uint64_t
|
||
|
st_swiss_repeat_byte(unsigned char byte)
|
||
|
{
|
||
|
return UINT64_C(0x0101010101010101) * byte;
|
||
|
}
|
||
|
static inline uint64_t
|
||
|
st_swiss_match_byte(uint64_t word, unsigned char byte)
|
||
|
{
|
||
|
uint64_t x = word ^ st_swiss_repeat_byte(byte);
|
||
|
return (x - UINT64_C(0x0101010101010101)) & ~x & UINT64_C(0x8080808080808080);
|
||
|
}
|
||
|
static inline uint64_t
|
||
|
st_swiss_ctrl_group(const st_table *tab, st_index_t ind)
|
||
|
{
|
||
|
return st_swiss_ctrl_groups(tab)[st_swiss_group_ind(ind)];
|
||
|
}
|
||
|
static inline st_index_t
|
||
|
st_swiss_group_start(st_hash_t hash_value, st_table *tab)
|
||
|
{
|
||
|
return hash_bin(hash_value, tab) & ~(st_index_t)(ST_SWISS_GROUP_SIZE - 1);
|
||
|
}
|
||
|
static inline st_index_t
|
||
|
st_swiss_next_group(st_index_t ind, st_table *tab)
|
||
|
{
|
||
|
return (ind + ST_SWISS_GROUP_SIZE) & bins_mask(tab);
|
||
|
}
|
||
|
typedef struct {
|
||
|
st_index_t bin_ind;
|
||
|
st_index_t entry_ind;
|
||
|
st_index_t first_deleted_bin_ind;
|
||
|
bool found;
|
||
|
bool rebuilt;
|
||
|
bool found_empty;
|
||
|
} st_swiss_probe_result_t;
|
||
|
#else
|
||
|
#define ST_GET_TABLE_BIN(tab, ind) get_bin((tab)->bins, get_size_ind(tab), (ind))
|
||
|
#define ST_SET_TABLE_BIN(tab, ind, bin) set_bin((tab)->bins, get_size_ind(tab), (ind), (bin))
|
||
|
#define ST_SET_TABLE_ENTRY_BIN(tab, ind, entry_ind, hash) set_bin((tab)->bins, get_size_ind(tab), (ind), (entry_ind) + ENTRY_BASE)
|
||
|
static inline st_index_t
|
||
|
st_bins_alloc_size(const st_table *tab)
|
||
|
{
|
||
|
return bins_size(tab);
|
||
|
}
|
||
|
#endif
|
||
|
static inline st_index_t
|
||
|
st_table_rebuild_bound(const st_table *tab)
|
||
|
{
|
||
|
#if ST_USE_SWISS_BINS
|
||
|
if (tab->bin_power > MAX_POWER2_FOR_TABLES_WITHOUT_BINS)
|
||
|
return st_swiss_rebuild_bound(tab);
|
||
|
#endif
|
||
|
return get_allocated_entries(tab);
|
||
|
}
|
||
|
#if ST_USE_SWISS_BINS
|
||
|
static inline bool
|
||
|
st_swiss_tombstone_rebuild_p(const st_table *tab, st_index_t bound)
|
||
|
{
|
||
|
st_index_t half_bound = bound / 2 + (bound & 1);
|
||
|
return tab->bins != NULL
|
||
|
&& tab->bin_power > MAX_POWER2_FOR_TABLES_WITHOUT_BINS
|
||
|
&& tab->num_entries < half_bound;
|
||
|
}
|
||
|
#endif
|
||
|
/* Mark all bins of table TAB as empty. */
|
||
|
static void
|
||
|
initialize_bins(st_table *tab)
|
||
|
{
|
||
|
#if ST_USE_SWISS_BINS
|
||
|
uint64_t *ctrl_groups = st_swiss_ctrl_groups(tab);
|
||
|
st_index_t i, groups_num = st_swiss_groups_num(tab);
|
||
|
memset(tab->bins, 0, get_bins_num(tab) * sizeof(st_index_t));
|
||
|
for (i = 0; i < groups_num; i++) {
|
||
|
ctrl_groups[i] = st_swiss_repeat_byte(ST_CTRL_EMPTY);
|
||
|
}
|
||
|
#else
|
||
|
memset(tab->bins, 0, bins_size(tab));
|
||
|
#endif
|
||
|
}
|
||
|
/* Make table TAB empty. */
|
||
| ... | ... | |
|
}
|
||
|
#endif
|
||
|
#if ST_USE_SWISS_BINS
|
||
|
if (size > ((st_index_t)1 << MAX_POWER2_FOR_TABLES_WITHOUT_BINS))
|
||
|
size = st_swiss_size_for_entries(size);
|
||
|
#endif
|
||
|
n = get_power2(size);
|
||
|
#ifndef RUBY
|
||
|
if (n < 0)
|
||
| ... | ... | |
|
tab->type = type;
|
||
|
tab->entry_power = n;
|
||
|
#if ST_USE_SWISS_BINS
|
||
|
tab->bin_power = n;
|
||
|
#else
|
||
|
tab->bin_power = features[n].bin_power;
|
||
|
#endif
|
||
|
tab->size_ind = features[n].size_ind;
|
||
|
if (n <= MAX_POWER2_FOR_TABLES_WITHOUT_BINS)
|
||
|
tab->bins = NULL;
|
||
|
else {
|
||
|
tab->bins = (st_index_t *) malloc(bins_size(tab));
|
||
|
tab->bins = NULL;
|
||
|
#if !ST_USE_SWISS_BINS
|
||
|
if (n > MAX_POWER2_FOR_TABLES_WITHOUT_BINS) {
|
||
|
tab->bins = (st_index_t *) malloc(st_bins_alloc_size(tab));
|
||
|
#ifndef RUBY
|
||
|
if (tab->bins == NULL) {
|
||
|
free_fixed_ptr(tab);
|
||
| ... | ... | |
|
}
|
||
|
#endif
|
||
|
}
|
||
|
tab->entries = (st_table_entry *) malloc(get_allocated_entries(tab)
|
||
|
* sizeof(st_table_entry));
|
||
|
#endif
|
||
|
tab->entries = (st_table_entry *) malloc(st_table_alloc_memsize(tab));
|
||
|
#ifndef RUBY
|
||
|
if (tab->entries == NULL) {
|
||
|
st_free_table(tab);
|
||
|
return NULL;
|
||
|
}
|
||
|
#endif
|
||
|
#if ST_USE_SWISS_BINS
|
||
|
st_enable_packed_bins(tab);
|
||
|
#endif
|
||
|
make_tab_empty(tab);
|
||
|
tab->rebuilds_num = 0;
|
||
| ... | ... | |
|
static inline size_t
|
||
|
st_entries_memsize(const st_table *tab)
|
||
|
{
|
||
|
return get_allocated_entries(tab) * sizeof(st_table_entry);
|
||
|
size_t memsize = get_allocated_entries(tab) * sizeof(st_table_entry);
|
||
|
#if ST_USE_SWISS_BINS
|
||
|
memsize += get_allocated_entries(tab) * sizeof(st_stored_hash_t);
|
||
|
#endif
|
||
|
return memsize;
|
||
|
}
|
||
|
static inline size_t
|
||
|
st_bins_memsize(const st_table *tab)
|
||
|
{
|
||
|
return tab->bins == NULL ? 0 : bins_size(tab);
|
||
|
#if ST_USE_SWISS_BINS
|
||
|
return (st_packs_bins(tab) || tab->bins != NULL) ? st_bins_alloc_size(tab) : 0;
|
||
|
#else
|
||
|
return tab->bins == NULL ? 0 : st_bins_alloc_size(tab);
|
||
|
#endif
|
||
|
}
|
||
|
static inline size_t
|
||
|
st_table_alloc_memsize(const st_table *tab)
|
||
|
{
|
||
|
size_t memsize = st_entries_memsize(tab);
|
||
|
#if ST_USE_SWISS_BINS
|
||
|
if (st_packs_bins(tab))
|
||
|
memsize += st_bins_alloc_size(tab);
|
||
|
#endif
|
||
|
return memsize;
|
||
|
}
|
||
|
static inline void
|
||
|
st_free_entries(const st_table *tab)
|
||
|
{
|
||
|
sized_free(tab->entries, st_entries_memsize(tab));
|
||
|
sized_free(tab->entries, st_table_alloc_memsize(tab));
|
||
|
}
|
||
|
static inline void
|
||
|
st_free_bins(const st_table *tab)
|
||
|
{
|
||
|
#if ST_USE_SWISS_BINS
|
||
|
if (tab->bins != NULL && tab->bins != st_packed_bins_ptr(tab))
|
||
|
sized_free(tab->bins, st_bins_alloc_size(tab));
|
||
|
#else
|
||
|
sized_free(tab->bins, st_bins_memsize(tab));
|
||
|
#endif
|
||
|
}
|
||
|
void
|
||
| ... | ... | |
|
#define FOUND_BIN
|
||
|
#endif
|
||
|
#if ST_USE_SWISS_BINS
|
||
|
static st_swiss_probe_result_t
|
||
|
st_swiss_probe(st_table *tab, st_hash_t hash_value, st_data_t key, bool reserve)
|
||
|
{
|
||
|
int eq_p, rebuilt_p;
|
||
|
st_index_t ind;
|
||
|
unsigned char h2 = st_swiss_h2(hash_value);
|
||
|
st_table_entry *entries = tab->entries;
|
||
|
st_swiss_probe_result_t result = {
|
||
|
UNDEFINED_BIN_IND,
|
||
|
UNDEFINED_ENTRY_IND,
|
||
|
UNDEFINED_BIN_IND,
|
||
|
false,
|
||
|
false,
|
||
|
false,
|
||
|
};
|
||
|
ind = st_swiss_group_start(hash_value, tab);
|
||
|
FOUND_BIN;
|
||
|
for (;;) {
|
||
|
uint64_t group = st_swiss_ctrl_group(tab, ind);
|
||
|
uint64_t candidates = st_swiss_match_byte(group, h2);
|
||
|
if (candidates != 0) do {
|
||
|
st_index_t bin_ind = ind + (ntz_int64(candidates) >> 3);
|
||
|
st_index_t bin = st_swiss_get_bin(tab, bin_ind);
|
||
|
st_index_t entry_ind = bin - ENTRY_BASE;
|
||
|
DO_PTR_EQUAL_CHECK(tab, &entries[entry_ind], hash_value, key, eq_p, rebuilt_p);
|
||
|
if (rebuilt_p) {
|
||
|
result.rebuilt = true;
|
||
|
return result;
|
||
|
}
|
||
|
if (eq_p) {
|
||
|
result.bin_ind = bin_ind;
|
||
|
result.entry_ind = entry_ind;
|
||
|
result.found = true;
|
||
|
return result;
|
||
|
}
|
||
|
candidates &= candidates - 1;
|
||
|
} while (candidates != 0);
|
||
|
if (reserve) {
|
||
|
uint64_t deleted = st_swiss_match_byte(group, ST_CTRL_DELETED);
|
||
|
if (result.first_deleted_bin_ind == UNDEFINED_BIN_IND && deleted != 0)
|
||
|
result.first_deleted_bin_ind = ind + (ntz_int64(deleted) >> 3);
|
||
|
}
|
||
|
{
|
||
|
uint64_t empty = st_swiss_match_byte(group, ST_CTRL_EMPTY);
|
||
|
if (empty != 0) {
|
||
|
result.found_empty = true;
|
||
|
result.bin_ind = ind + (ntz_int64(empty) >> 3);
|
||
|
if (reserve && result.first_deleted_bin_ind != UNDEFINED_BIN_IND)
|
||
|
result.bin_ind = result.first_deleted_bin_ind;
|
||
|
assert(!reserve || result.bin_ind != UNDEFINED_BIN_IND);
|
||
|
return result;
|
||
|
}
|
||
|
}
|
||
|
ind = st_swiss_next_group(ind, tab);
|
||
|
COLLISION;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
/* If the number of entries in the table is at least REBUILD_THRESHOLD
|
||
|
times less than the entry array length, decrease the table
|
||
|
size. */
|
||
| ... | ... | |
|
rebuild_table_with(st_table *const new_tab, st_table *const tab)
|
||
|
{
|
||
|
st_index_t i, ni;
|
||
|
#if !ST_USE_SWISS_BINS
|
||
|
unsigned int size_ind;
|
||
|
#endif
|
||
|
st_table_entry *new_entries;
|
||
|
st_table_entry *curr_entry_ptr;
|
||
|
st_index_t *bins;
|
||
| ... | ... | |
|
ni = 0;
|
||
|
bins = new_tab->bins;
|
||
|
#if !ST_USE_SWISS_BINS
|
||
|
size_ind = get_size_ind(new_tab);
|
||
|
#endif
|
||
|
st_index_t bound = tab->entries_bound;
|
||
|
st_table_entry *entries = tab->entries;
|
||
|
for (i = tab->entries_start; i < bound; i++) {
|
||
|
curr_entry_ptr = &entries[i];
|
||
|
PREFETCH(entries + i + 1, 0);
|
||
|
if (EXPECT(DELETED_ENTRY_P(curr_entry_ptr), 0))
|
||
|
if (EXPECT(DELETED_ENTRY_P(tab, curr_entry_ptr), 0))
|
||
|
continue;
|
||
|
if (&new_entries[ni] != curr_entry_ptr)
|
||
|
new_entries[ni] = *curr_entry_ptr;
|
||
|
ST_SET_HASH_AT_IDX(new_tab, ni, ST_HASH_AT_IDX(tab, i));
|
||
|
if (EXPECT(bins != NULL, 1)) {
|
||
|
bin_ind = find_table_bin_ind_direct(new_tab, curr_entry_ptr->hash,
|
||
|
bin_ind = find_table_bin_ind_direct(new_tab, ST_HASH_AT_IDX(new_tab, ni),
|
||
|
curr_entry_ptr->key);
|
||
|
#if ST_USE_SWISS_BINS
|
||
|
st_swiss_set_entry_bin(new_tab, bin_ind, ni, ST_HASH_AT_IDX(new_tab, ni));
|
||
|
#else
|
||
|
set_bin(bins, size_ind, bin_ind, ni + ENTRY_BASE);
|
||
|
#endif
|
||
|
}
|
||
|
new_tab->num_entries++;
|
||
|
ni++;
|
||
| ... | ... | |
|
static st_index_t
|
||
|
find_table_entry_ind(st_table *tab, st_hash_t hash_value, st_data_t key)
|
||
|
{
|
||
|
#if ST_USE_SWISS_BINS
|
||
|
st_swiss_probe_result_t result = st_swiss_probe(tab, hash_value, key, false);
|
||
|
if (EXPECT(result.rebuilt, 0))
|
||
|
return REBUILT_TABLE_ENTRY_IND;
|
||
|
if (!result.found)
|
||
|
return UNDEFINED_ENTRY_IND;
|
||
|
assert(result.entry_ind != UNDEFINED_ENTRY_IND);
|
||
|
return result.entry_ind + ENTRY_BASE;
|
||
|
#else
|
||
|
int eq_p, rebuilt_p;
|
||
|
st_index_t ind;
|
||
|
#ifdef QUADRATIC_PROBE
|
||
| ... | ... | |
|
COLLISION;
|
||
|
}
|
||
|
return bin;
|
||
|
#endif
|
||
|
}
|
||
|
/* Find and return index of table TAB bin corresponding to an entry
|
||
| ... | ... | |
|
static st_index_t
|
||
|
find_table_bin_ind(st_table *tab, st_hash_t hash_value, st_data_t key)
|
||
|
{
|
||
|
#if ST_USE_SWISS_BINS
|
||
|
st_swiss_probe_result_t result = st_swiss_probe(tab, hash_value, key, false);
|
||
|
if (EXPECT(result.rebuilt, 0))
|
||
|
return REBUILT_TABLE_BIN_IND;
|
||
|
if (!result.found)
|
||
|
return UNDEFINED_BIN_IND;
|
||
|
assert(result.bin_ind != UNDEFINED_BIN_IND);
|
||
|
return result.bin_ind;
|
||
|
#else
|
||
|
int eq_p, rebuilt_p;
|
||
|
st_index_t ind;
|
||
|
#ifdef QUADRATIC_PROBE
|
||
| ... | ... | |
|
COLLISION;
|
||
|
}
|
||
|
return ind;
|
||
|
#endif
|
||
|
}
|
||
|
/* Find and return index of table TAB bin corresponding to an entry
|
||
| ... | ... | |
|
static st_index_t
|
||
|
find_table_bin_ind_direct(st_table *tab, st_hash_t hash_value, st_data_t key)
|
||
|
{
|
||
|
#if ST_USE_SWISS_BINS
|
||
|
st_index_t ind;
|
||
|
(void)key;
|
||
|
ind = st_swiss_group_start(hash_value, tab);
|
||
|
FOUND_BIN;
|
||
|
for (;;) {
|
||
|
uint64_t group = st_swiss_ctrl_group(tab, ind);
|
||
|
uint64_t candidates = st_swiss_match_byte(group, ST_CTRL_EMPTY)
|
||
|
| st_swiss_match_byte(group, ST_CTRL_DELETED);
|
||
|
if (candidates != 0)
|
||
|
return ind + (ntz_int64(candidates) >> 3);
|
||
|
ind = st_swiss_next_group(ind, tab);
|
||
|
COLLISION;
|
||
|
}
|
||
|
#else
|
||
|
st_index_t ind;
|
||
|
#ifdef QUADRATIC_PROBE
|
||
|
st_index_t d;
|
||
| ... | ... | |
|
#endif
|
||
|
COLLISION;
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
/* Return index of table TAB bin for HASH_VALUE and KEY through
|
||
| ... | ... | |
|
bin for inclusion of the corresponding entry into the table if it
|
||
|
is not there yet. We always find such bin as bins array length is
|
||
|
bigger entries array. Although we can reuse a deleted bin, the
|
||
|
result bin value is always empty if the table has no entry with
|
||
|
KEY. Return the entries array index of the found entry or
|
||
|
non-Swiss result bin value is always empty if the table has no entry
|
||
|
with KEY. Swiss bins may return a deleted bin for immediate overwrite.
|
||
|
Return the entries array index of the found entry or
|
||
|
UNDEFINED_ENTRY_IND if it is not found. If the table was rebuilt
|
||
|
during the search, return REBUILT_TABLE_ENTRY_IND. */
|
||
|
static st_index_t
|
||
|
find_table_bin_ptr_and_reserve(st_table *tab, st_hash_t *hash_value,
|
||
|
st_data_t key, st_index_t *bin_ind)
|
||
|
{
|
||
|
#if ST_USE_SWISS_BINS
|
||
|
st_swiss_probe_result_t result = st_swiss_probe(tab, *hash_value, key, true);
|
||
|
if (EXPECT(result.rebuilt, 0))
|
||
|
return REBUILT_TABLE_ENTRY_IND;
|
||
|
*bin_ind = result.bin_ind;
|
||
|
if (result.found) {
|
||
|
assert(result.entry_ind != UNDEFINED_ENTRY_IND);
|
||
|
return result.entry_ind + ENTRY_BASE;
|
||
|
}
|
||
|
assert(result.found_empty);
|
||
|
assert(*bin_ind != UNDEFINED_BIN_IND);
|
||
|
tab->num_entries++;
|
||
|
return UNDEFINED_ENTRY_IND;
|
||
|
#else
|
||
|
int eq_p, rebuilt_p;
|
||
|
st_index_t ind;
|
||
|
st_hash_t curr_hash_value = *hash_value;
|
||
| ... | ... | |
|
}
|
||
|
*bin_ind = ind;
|
||
|
return entry_index;
|
||
|
#endif
|
||
|
}
|
||
|
/* Find an entry with KEY in table TAB. Return non-zero if we found
|
||
| ... | ... | |
|
{
|
||
|
st_index_t bound = tab->entries_bound;
|
||
|
if (bound == get_allocated_entries(tab))
|
||
|
if (bound >= st_table_rebuild_bound(tab)
|
||
|
#if ST_USE_SWISS_BINS
|
||
|
|| st_swiss_tombstone_rebuild_p(tab, bound)
|
||
|
#endif
|
||
|
)
|
||
|
rebuild_table(tab);
|
||
|
}
|
||
| ... | ... | |
|
if (new_p) {
|
||
|
ind = tab->entries_bound++;
|
||
|
entry = &tab->entries[ind];
|
||
|
entry->hash = hash_value;
|
||
|
ST_SET_HASH_AT_IDX(tab, ind, hash_value);
|
||
|
entry->key = key;
|
||
|
entry->record = value;
|
||
|
if (bin_ind != UNDEFINED_BIN_IND)
|
||
|
set_bin(tab->bins, get_size_ind(tab), bin_ind, ind + ENTRY_BASE);
|
||
|
if (bin_ind != UNDEFINED_BIN_IND) {
|
||
|
ST_SET_TABLE_ENTRY_BIN(tab, bin_ind, ind, hash_value);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
tab->entries[bin].record = value;
|
||
| ... | ... | |
|
st_index_t ind;
|
||
|
st_index_t bin_ind;
|
||
|
assert(hash != RESERVED_HASH_VAL);
|
||
|
assert(hash != ST_RESERVED_HASH_VAL);
|
||
|
rebuild_table_if_necessary(tab);
|
||
|
ind = tab->entries_bound++;
|
||
|
entry = &tab->entries[ind];
|
||
|
entry->hash = hash;
|
||
|
ST_SET_HASH_AT_IDX(tab, ind, hash);
|
||
|
entry->key = key;
|
||
|
entry->record = value;
|
||
|
tab->num_entries++;
|
||
|
if (tab->bins != NULL) {
|
||
|
bin_ind = find_table_bin_ind_direct(tab, hash, key);
|
||
|
set_bin(tab->bins, get_size_ind(tab), bin_ind, ind + ENTRY_BASE);
|
||
|
ST_SET_TABLE_ENTRY_BIN(tab, bin_ind, ind, hash);
|
||
|
}
|
||
|
}
|
||
| ... | ... | |
|
rb_st_add_direct_with_hash(st_table *tab,
|
||
|
st_data_t key, st_data_t value, st_hash_t hash)
|
||
|
{
|
||
|
st_add_direct_with_hash(tab, key, value, normalize_hash_value(hash));
|
||
|
st_add_direct_with_hash(tab, key, value, st_normalize_hash_value(hash));
|
||
|
}
|
||
|
/* Insert (KEY, VALUE) into table TAB. The table should not have
|
||
| ... | ... | |
|
key = (*func)(key);
|
||
|
ind = tab->entries_bound++;
|
||
|
entry = &tab->entries[ind];
|
||
|
entry->hash = hash_value;
|
||
|
ST_SET_HASH_AT_IDX(tab, ind, hash_value);
|
||
|
entry->key = key;
|
||
|
entry->record = value;
|
||
|
if (bin_ind != UNDEFINED_BIN_IND)
|
||
|
set_bin(tab->bins, get_size_ind(tab), bin_ind, ind + ENTRY_BASE);
|
||
|
if (bin_ind != UNDEFINED_BIN_IND) {
|
||
|
ST_SET_TABLE_ENTRY_BIN(tab, bin_ind, ind, hash_value);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
tab->entries[bin].record = value;
|
||
| ... | ... | |
|
st_replace(st_table *new_tab, st_table *old_tab)
|
||
|
{
|
||
|
*new_tab = *old_tab;
|
||
|
#if ST_USE_SWISS_BINS
|
||
|
new_tab->entries = (st_table_entry *) malloc(st_table_alloc_memsize(old_tab));
|
||
|
#ifndef RUBY
|
||
|
if (new_tab->entries == NULL) {
|
||
|
return NULL;
|
||
|
}
|
||
|
#endif
|
||
|
if (old_tab->bins != NULL) {
|
||
|
st_enable_packed_bins(new_tab);
|
||
|
if (new_tab->bins == NULL)
|
||
|
new_tab->bins = (st_index_t *) malloc(st_bins_alloc_size(old_tab));
|
||
|
#ifndef RUBY
|
||
|
if (new_tab->bins == NULL) {
|
||
|
return NULL;
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
MEMCPY(new_tab->entries, old_tab->entries, char, st_entries_memsize(old_tab));
|
||
|
if (old_tab->bins != NULL)
|
||
|
MEMCPY(new_tab->bins, old_tab->bins, char, st_bins_alloc_size(old_tab));
|
||
|
#else
|
||
|
if (old_tab->bins == NULL)
|
||
|
new_tab->bins = NULL;
|
||
|
else {
|
||
|
new_tab->bins = (st_index_t *) malloc(bins_size(old_tab));
|
||
|
new_tab->bins = (st_index_t *) malloc(st_bins_alloc_size(old_tab));
|
||
|
#ifndef RUBY
|
||
|
if (new_tab->bins == NULL) {
|
||
|
return NULL;
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
new_tab->entries = (st_table_entry *) malloc(get_allocated_entries(old_tab)
|
||
|
* sizeof(st_table_entry));
|
||
|
new_tab->entries = (st_table_entry *) malloc(st_entries_memsize(old_tab));
|
||
|
#ifndef RUBY
|
||
|
if (new_tab->entries == NULL) {
|
||
|
return NULL;
|
||
|
}
|
||
|
#endif
|
||
|
MEMCPY(new_tab->entries, old_tab->entries, st_table_entry,
|
||
|
get_allocated_entries(old_tab));
|
||
|
MEMCPY(new_tab->entries, old_tab->entries, char, st_entries_memsize(old_tab));
|
||
|
if (old_tab->bins != NULL)
|
||
|
MEMCPY(new_tab->bins, old_tab->bins, char, bins_size(old_tab));
|
||
|
MEMCPY(new_tab->bins, old_tab->bins, char, st_bins_alloc_size(old_tab));
|
||
|
#endif
|
||
|
return new_tab;
|
||
|
}
|
||
| ... | ... | |
|
st_index_t start = n + 1;
|
||
|
st_index_t bound = tab->entries_bound;
|
||
|
st_table_entry *entries = tab->entries;
|
||
|
while (start < bound && DELETED_ENTRY_P(&entries[start])) start++;
|
||
|
while (start < bound && DELETED_ENTRY_P(tab, &entries[start])) start++;
|
||
|
tab->entries_start = start;
|
||
|
}
|
||
|
}
|
||
| ... | ... | |
|
if (value != 0) *value = 0;
|
||
|
return 0;
|
||
|
}
|
||
|
bin = get_bin(tab->bins, get_size_ind(tab), bin_ind) - ENTRY_BASE;
|
||
|
bin = ST_GET_TABLE_BIN(tab, bin_ind) - ENTRY_BASE;
|
||
|
MARK_BIN_DELETED(tab, bin_ind);
|
||
|
}
|
||
|
entry = &tab->entries[bin];
|
||
|
*key = entry->key;
|
||
|
if (value != 0) *value = entry->record;
|
||
|
MARK_ENTRY_DELETED(entry);
|
||
|
MARK_ENTRY_DELETED(tab, entry);
|
||
|
tab->num_entries--;
|
||
|
update_range_for_deleted(tab, bin);
|
||
|
return 1;
|
||
| ... | ... | |
|
bound = tab->entries_bound;
|
||
|
for (i = tab->entries_start; i < bound; i++) {
|
||
|
curr_entry_ptr = &entries[i];
|
||
|
if (! DELETED_ENTRY_P(curr_entry_ptr)) {
|
||
|
st_hash_t entry_hash = curr_entry_ptr->hash;
|
||
|
if (! DELETED_ENTRY_P(tab, curr_entry_ptr)) {
|
||
|
st_hash_t entry_hash = ST_HASH_AT_PTR(tab, curr_entry_ptr);
|
||
|
st_data_t entry_key = curr_entry_ptr->key;
|
||
|
if (value != 0) *value = curr_entry_ptr->record;
|
||
| ... | ... | |
|
entries = tab->entries;
|
||
|
goto retry;
|
||
|
}
|
||
|
curr_entry_ptr = &entries[get_bin(tab->bins, get_size_ind(tab), bin_ind)
|
||
|
- ENTRY_BASE];
|
||
|
curr_entry_ptr = &entries[ST_GET_TABLE_BIN(tab, bin_ind) - ENTRY_BASE];
|
||
|
MARK_BIN_DELETED(tab, bin_ind);
|
||
|
}
|
||
|
MARK_ENTRY_DELETED(curr_entry_ptr);
|
||
|
MARK_ENTRY_DELETED(tab, curr_entry_ptr);
|
||
|
tab->num_entries--;
|
||
|
update_range_for_deleted(tab, i);
|
||
|
return 1;
|
||
| ... | ... | |
|
goto retry;
|
||
|
existing = bin_ind != UNDEFINED_BIN_IND;
|
||
|
if (existing) {
|
||
|
bin = get_bin(tab->bins, get_size_ind(tab), bin_ind) - ENTRY_BASE;
|
||
|
bin = ST_GET_TABLE_BIN(tab, bin_ind) - ENTRY_BASE;
|
||
|
entry = &entries[bin];
|
||
|
}
|
||
|
}
|
||
| ... | ... | |
|
if (existing) {
|
||
|
if (bin_ind != UNDEFINED_BIN_IND)
|
||
|
MARK_BIN_DELETED(tab, bin_ind);
|
||
|
MARK_ENTRY_DELETED(entry);
|
||
|
MARK_ENTRY_DELETED(tab, entry);
|
||
|
tab->num_entries--;
|
||
|
update_range_for_deleted(tab, bin);
|
||
|
}
|
||
| ... | ... | |
|
the table, e.g. by an entry insertion. */
|
||
|
for (i = tab->entries_start; i < tab->entries_bound; i++) {
|
||
|
curr_entry_ptr = &entries[i];
|
||
|
if (EXPECT(DELETED_ENTRY_P(curr_entry_ptr), 0))
|
||
|
if (EXPECT(DELETED_ENTRY_P(tab, curr_entry_ptr), 0))
|
||
|
continue;
|
||
|
key = curr_entry_ptr->key;
|
||
|
rebuilds_num = tab->rebuilds_num;
|
||
|
hash = curr_entry_ptr->hash;
|
||
|
hash = ST_HASH_AT_PTR(tab, curr_entry_ptr);
|
||
|
retval = (*func)(key, curr_entry_ptr->record, arg, 0);
|
||
|
if (retval == ST_REPLACE && replace) {
|
||
| ... | ... | |
|
goto again;
|
||
|
if (bin_ind == UNDEFINED_BIN_IND)
|
||
|
break;
|
||
|
bin = get_bin(tab->bins, get_size_ind(tab), bin_ind) - ENTRY_BASE;
|
||
|
bin = ST_GET_TABLE_BIN(tab, bin_ind) - ENTRY_BASE;
|
||
|
MARK_BIN_DELETED(tab, bin_ind);
|
||
|
}
|
||
|
curr_entry_ptr = &entries[bin];
|
||
|
MARK_ENTRY_DELETED(curr_entry_ptr);
|
||
|
MARK_ENTRY_DELETED(tab, curr_entry_ptr);
|
||
|
tab->num_entries--;
|
||
|
update_range_for_deleted(tab, bin);
|
||
|
break;
|
||
| ... | ... | |
|
break;
|
||
|
curr_entry_ptr = &entries[i];
|
||
|
key = curr_entry_ptr->key;
|
||
|
if (! DELETED_ENTRY_P(curr_entry_ptr))
|
||
|
if (! DELETED_ENTRY_P(tab, curr_entry_ptr))
|
||
|
*keys++ = key;
|
||
|
}
|
||
| ... | ... | |
|
if (values == values_end)
|
||
|
break;
|
||
|
curr_entry_ptr = &entries[i];
|
||
|
if (! DELETED_ENTRY_P(curr_entry_ptr))
|
||
|
if (! DELETED_ENTRY_P(tab, curr_entry_ptr))
|
||
|
*values++ = curr_entry_ptr->record;
|
||
|
}
|
||
| ... | ... | |
|
st_table *tmp;
|
||
|
st_index_t n;
|
||
|
if (siz <= get_allocated_entries(tab))
|
||
|
if (siz <= st_table_rebuild_bound(tab))
|
||
|
return; /* enough room already */
|
||
|
tmp = st_init_table_with_size(tab->type, siz);
|
||
|
n = get_allocated_entries(tab);
|
||
|
MEMCPY(tmp->entries, tab->entries, st_table_entry, n);
|
||
|
#if ST_USE_SWISS_BINS
|
||
|
MEMCPY(st_hashes_ptr(tmp), st_hashes_ptr(tab), st_stored_hash_t, n);
|
||
|
#endif
|
||
|
st_free_bins(tab);
|
||
|
st_free_entries(tab);
|
||
|
st_free_bins(tmp);
|
||
| ... | ... | |
|
for (i = tab->entries_start; i < tab->entries_bound; i++) {
|
||
|
p = &tab->entries[i];
|
||
|
if (DELETED_ENTRY_P(p))
|
||
|
if (DELETED_ENTRY_P(tab, p))
|
||
|
continue;
|
||
|
for (j = i + 1; j < tab->entries_bound; j++) {
|
||
|
q = &tab->entries[j];
|
||
|
if (DELETED_ENTRY_P(q))
|
||
|
if (DELETED_ENTRY_P(tab, q))
|
||
|
continue;
|
||
|
DO_PTR_EQUAL_CHECK(tab, p, q->hash, q->key, eq_p, rebuilt_p);
|
||
|
DO_PTR_EQUAL_CHECK(tab, p, ST_HASH_AT_PTR(tab, q), q->key, eq_p, rebuilt_p);
|
||
|
if (EXPECT(rebuilt_p, 0))
|
||
|
return TRUE;
|
||
|
if (eq_p) {
|
||
|
*p = *q;
|
||
|
MARK_ENTRY_DELETED(q);
|
||
|
ST_SET_HASH_AT_PTR(tab, p, ST_HASH_AT_PTR(tab, q));
|
||
|
MARK_ENTRY_DELETED(tab, q);
|
||
|
tab->num_entries--;
|
||
|
update_range_for_deleted(tab, j);
|
||
|
}
|
||
| ... | ... | |
|
st_index_t i;
|
||
|
if (!tab->bins) {
|
||
|
tab->bins = malloc(bins_size(tab));
|
||
|
#if ST_USE_SWISS_BINS
|
||
|
st_enable_packed_bins(tab);
|
||
|
if (tab->bins == NULL)
|
||
|
tab->bins = malloc(st_bins_alloc_size(tab));
|
||
|
#else
|
||
|
tab->bins = malloc(st_bins_alloc_size(tab));
|
||
|
#endif
|
||
|
}
|
||
|
unsigned int const size_ind = get_size_ind(tab);
|
||
|
initialize_bins(tab);
|
||
|
for (i = tab->entries_start; i < tab->entries_bound; i++) {
|
||
|
st_table_entry *p = &tab->entries[i];
|
||
|
st_hash_t p_hash = ST_HASH_AT_PTR(tab, p);
|
||
|
st_index_t ind;
|
||
|
#ifdef QUADRATIC_PROBE
|
||
|
st_index_t d = 1;
|
||
|
#else
|
||
|
st_index_t perturb = p->hash;
|
||
|
st_index_t perturb = p_hash;
|
||
|
#endif
|
||
|
if (DELETED_ENTRY_P(p))
|
||
|
if (DELETED_ENTRY_P(tab, p))
|
||
|
continue;
|
||
|
ind = hash_bin(p->hash, tab);
|
||
|
#if ST_USE_SWISS_BINS
|
||
|
{
|
||
|
st_index_t bin_ind = find_table_bin_ind(tab, p_hash, p->key);
|
||
|
if (EXPECT(bin_ind == REBUILT_TABLE_BIN_IND, 0))
|
||
|
return TRUE;
|
||
|
if (bin_ind == UNDEFINED_BIN_IND) {
|
||
|
bin_ind = find_table_bin_ind_direct(tab, p_hash, p->key);
|
||
|
st_swiss_set_entry_bin(tab, bin_ind, i, p_hash);
|
||
|
}
|
||
|
else {
|
||
|
st_index_t bin = st_swiss_get_bin(tab, bin_ind);
|
||
|
st_table_entry *q = &tab->entries[bin - ENTRY_BASE];
|
||
|
q->record = p->record;
|
||
|
MARK_ENTRY_DELETED(tab, p);
|
||
|
tab->num_entries--;
|
||
|
update_range_for_deleted(tab, i);
|
||
|
}
|
||
|
continue;
|
||
|
}
|
||
|
#endif
|
||
|
ind = hash_bin(p_hash, tab);
|
||
|
for (;;) {
|
||
|
st_index_t bin = get_bin(tab->bins, size_ind, ind);
|
||
|
if (EMPTY_OR_DELETED_BIN_P(bin)) {
|
||
| ... | ... | |
|
}
|
||
|
else {
|
||
|
st_table_entry *q = &tab->entries[bin - ENTRY_BASE];
|
||
|
DO_PTR_EQUAL_CHECK(tab, q, p->hash, p->key, eq_p, rebuilt_p);
|
||
|
DO_PTR_EQUAL_CHECK(tab, q, p_hash, p->key, eq_p, rebuilt_p);
|
||
|
if (EXPECT(rebuilt_p, 0))
|
||
|
return TRUE;
|
||
|
if (eq_p) {
|
||
|
/* duplicated key; delete it */
|
||
|
q->record = p->record;
|
||
|
MARK_ENTRY_DELETED(p);
|
||
|
MARK_ENTRY_DELETED(tab, p);
|
||
|
tab->num_entries--;
|
||
|
update_range_for_deleted(tab, bin);
|
||
|
break;
|
||
| ... | ... | |
|
st_insert_single(st_table *tab, VALUE hash, VALUE key, VALUE val)
|
||
|
{
|
||
|
st_data_t k = st_stringify(key);
|
||
|
st_hash_t h = do_hash(k, tab);
|
||
|
st_table_entry e;
|
||
|
e.hash = do_hash(k, tab);
|
||
|
e.key = k;
|
||
|
e.record = val;
|
||
|
tab->entries[tab->entries_bound++] = e;
|
||
|
ST_SET_HASH_AT_IDX(tab, tab->entries_bound - 1, h);
|
||
|
tab->num_entries++;
|
||
|
RB_OBJ_WRITTEN(hash, Qundef, k);
|
||
|
RB_OBJ_WRITTEN(hash, Qundef, val);
|
||
| ... | ... | |
|
for (i = tab->entries_start; i < bound; i++) {
|
||
|
curr_entry_ptr = &entries[i];
|
||
|
PREFETCH(entries + i + 1, 0);
|
||
|
if (EXPECT(DELETED_ENTRY_P(curr_entry_ptr), 0))
|
||
|
if (EXPECT(SET_DELETED_ENTRY_P(curr_entry_ptr), 0))
|
||
|
continue;
|
||
|
if (&new_entries[ni] != curr_entry_ptr)
|
||
|
new_entries[ni] = *curr_entry_ptr;
|
||
| ... | ... | |
|
bound = tab->entries_bound;
|
||
|
entries = tab->entries;
|
||
|
for (i = tab->entries_start; i < bound; i++) {
|
||
|
DO_PTR_EQUAL_CHECK(tab, &entries[i], hash_value, key, eq_p, rebuilt_p);
|
||
|
SET_DO_PTR_EQUAL_CHECK(tab, &entries[i], hash_value, key, eq_p, rebuilt_p);
|
||
|
if (EXPECT(rebuilt_p, 0))
|
||
|
return REBUILT_TABLE_ENTRY_IND;
|
||
|
if (eq_p)
|
||
| ... | ... | |
|
for (;;) {
|
||
|
bin = get_bin(set_bins_ptr(tab), set_get_size_ind(tab), ind);
|
||
|
if (! EMPTY_OR_DELETED_BIN_P(bin)) {
|
||
|
DO_PTR_EQUAL_CHECK(tab, &entries[bin - ENTRY_BASE], hash_value, key, eq_p, rebuilt_p);
|
||
|
SET_DO_PTR_EQUAL_CHECK(tab, &entries[bin - ENTRY_BASE], hash_value, key, eq_p, rebuilt_p);
|
||
|
if (EXPECT(rebuilt_p, 0))
|
||
|
return REBUILT_TABLE_ENTRY_IND;
|
||
|
if (eq_p)
|
||
| ... | ... | |
|
for (;;) {
|
||
|
bin = get_bin(set_bins_ptr(tab), set_get_size_ind(tab), ind);
|
||
|
if (! EMPTY_OR_DELETED_BIN_P(bin)) {
|
||
|
DO_PTR_EQUAL_CHECK(tab, &entries[bin - ENTRY_BASE], hash_value, key, eq_p, rebuilt_p);
|
||
|
SET_DO_PTR_EQUAL_CHECK(tab, &entries[bin - ENTRY_BASE], hash_value, key, eq_p, rebuilt_p);
|
||
|
if (EXPECT(rebuilt_p, 0))
|
||
|
return REBUILT_TABLE_BIN_IND;
|
||
|
if (eq_p)
|
||
| ... | ... | |
|
break;
|
||
|
}
|
||
|
else if (! DELETED_BIN_P(entry_index)) {
|
||
|
DO_PTR_EQUAL_CHECK(tab, &entries[entry_index - ENTRY_BASE], curr_hash_value, key, eq_p, rebuilt_p);
|
||
|
SET_DO_PTR_EQUAL_CHECK(tab, &entries[entry_index - ENTRY_BASE], curr_hash_value, key, eq_p, rebuilt_p);
|
||
|
if (EXPECT(rebuilt_p, 0))
|
||
|
return REBUILT_TABLE_ENTRY_IND;
|
||
|
if (eq_p)
|
||
| ... | ... | |
|
st_index_t start = n + 1;
|
||
|
st_index_t bound = tab->entries_bound;
|
||
|
set_table_entry *entries = tab->entries;
|
||
|
while (start < bound && DELETED_ENTRY_P(&entries[start])) start++;
|
||
|
while (start < bound && SET_DELETED_ENTRY_P(&entries[start])) start++;
|
||
|
tab->entries_start = start;
|
||
|
}
|
||
|
}
|
||
| ... | ... | |
|
}
|
||
|
entry = &tab->entries[bin];
|
||
|
*key = entry->key;
|
||
|
MARK_ENTRY_DELETED(entry);
|
||
|
SET_MARK_ENTRY_DELETED(entry);
|
||
|
tab->num_entries--;
|
||
|
set_update_range_for_deleted(tab, bin);
|
||
|
return 1;
|
||
| ... | ... | |
|
the table, e.g. by an entry insertion. */
|
||
|
for (i = tab->entries_start; i < tab->entries_bound; i++) {
|
||
|
curr_entry_ptr = &entries[i];
|
||
|
if (EXPECT(DELETED_ENTRY_P(curr_entry_ptr), 0))
|
||
|
if (EXPECT(SET_DELETED_ENTRY_P(curr_entry_ptr), 0))
|
||
|
continue;
|
||
|
key = curr_entry_ptr->key;
|
||
|
rebuilds_num = tab->rebuilds_num;
|
||
| ... | ... | |
|
MARK_SET_BIN_DELETED(tab, bin_ind);
|
||
|
}
|
||
|
curr_entry_ptr = &entries[bin];
|
||
|
MARK_ENTRY_DELETED(curr_entry_ptr);
|
||
|
SET_MARK_ENTRY_DELETED(curr_entry_ptr);
|
||
|
tab->num_entries--;
|
||
|
set_update_range_for_deleted(tab, bin);
|
||
|
break;
|
||
| ... | ... | |
|
break;
|
||
|
curr_entry_ptr = &entries[i];
|
||
|
key = curr_entry_ptr->key;
|
||
|
if (! DELETED_ENTRY_P(curr_entry_ptr))
|
||
|
if (! SET_DELETED_ENTRY_P(curr_entry_ptr))
|
||
|
*keys++ = key;
|
||
|
}
|
||