Feature #12142 » base.patch
benchmark/bm_hash_aref_dsym.rb | ||
---|---|---|
h = {}
|
||
syms = ('a'..'z').map { |s| s.to_sym }
|
||
syms.each { |s| h[s] = 1 }
|
||
200_000.times { syms.each { |s| h[s] } }
|
||
400_000.times { syms.each { |s| h[s] } }
|
benchmark/bm_hash_aref_dsym_long.rb | ||
---|---|---|
rng = Random.new(0)
|
||
symbol_sample_array = values.sample(sample_size, random: rng).map(&:to_sym)
|
||
3000.times do
|
||
1000.times do
|
||
symbol_sample_array.each { |x| symbol_hash[x] }
|
||
end
|
benchmark/bm_hash_aref_fix.rb | ||
---|---|---|
h = {}
|
||
nums = (1..26).to_a
|
||
nums.each { |i| h[i] = i }
|
||
200_000.times { nums.each { |s| h[s] } }
|
||
800_000.times { nums.each { |s| h[s] } }
|
benchmark/bm_hash_aref_flo.rb | ||
---|---|---|
h = {}
|
||
strs = [*1..10000].map! {|i| i.fdiv(10)}
|
||
strs.each { |s| h[s] = s }
|
||
50.times { strs.each { |s| h[s] } }
|
||
500.times { strs.each { |s| h[s] } }
|
benchmark/bm_hash_aref_miss.rb | ||
---|---|---|
strs = ('a'..'z').to_a.map!(&:freeze)
|
||
strs.each { |s| h[s] = s }
|
||
strs = ('A'..'Z').to_a
|
||
200_000.times { strs.each { |s| h[s] } }
|
||
500_000.times { strs.each { |s| h[s] } }
|
benchmark/bm_hash_aref_str.rb | ||
---|---|---|
h = {}
|
||
strs = ('a'..'z').to_a.map!(&:freeze)
|
||
strs.each { |s| h[s] = s }
|
||
200_000.times { strs.each { |s| h[s] } }
|
||
500_000.times { strs.each { |s| h[s] } }
|
benchmark/bm_hash_aref_sym.rb | ||
---|---|---|
syms.map!(&:to_sym)
|
||
end
|
||
syms.each { |s| h[s] = s }
|
||
200_000.times { syms.each { |s| h[s] } }
|
||
500_000.times { syms.each { |s| h[s] } }
|
benchmark/bm_hash_aref_sym_long.rb | ||
---|---|---|
syms.map!(&:to_sym)
|
||
end
|
||
syms.each { |s| h[s] = s }
|
||
200_000.times { syms.each { |s| h[s] } }
|
||
500_000.times { syms.each { |s| h[s] } }
|
benchmark/bm_hash_flatten.rb | ||
---|---|---|
h[i] = nil
|
||
end
|
||
1000.times do
|
||
2000.times do
|
||
h.flatten
|
||
end
|
benchmark/bm_hash_ident_flo.rb | ||
---|---|---|
h = {}.compare_by_identity
|
||
strs = (1..10000).to_a.map!(&:to_f)
|
||
strs.each { |s| h[s] = s }
|
||
50.times { strs.each { |s| h[s] } }
|
||
500.times { strs.each { |s| h[s] } }
|
benchmark/bm_hash_ident_num.rb | ||
---|---|---|
h = {}.compare_by_identity
|
||
nums = (1..26).to_a
|
||
nums.each { |n| h[n] = n }
|
||
200_000.times { nums.each { |n| h[n] } }
|
||
500_000.times { nums.each { |n| h[n] } }
|
benchmark/bm_hash_ident_obj.rb | ||
---|---|---|
h = {}.compare_by_identity
|
||
objs = 26.times.map { Object.new }
|
||
objs.each { |o| h[o] = o }
|
||
200_000.times { objs.each { |o| h[o] } }
|
||
500_000.times { objs.each { |o| h[o] } }
|
benchmark/bm_hash_ident_str.rb | ||
---|---|---|
h = {}.compare_by_identity
|
||
strs = ('a'..'z').to_a
|
||
strs.each { |s| h[s] = s }
|
||
200_000.times { strs.each { |s| h[s] } }
|
||
500_000.times { strs.each { |s| h[s] } }
|
benchmark/bm_hash_ident_sym.rb | ||
---|---|---|
h = {}.compare_by_identity
|
||
syms = ('a'..'z').to_a.map(&:to_sym)
|
||
syms.each { |s| h[s] = s }
|
||
200_000.times { syms.each { |s| h[s] } }
|
||
500_000.times { syms.each { |s| h[s] } }
|
benchmark/bm_hash_keys.rb | ||
---|---|---|
h[i] = nil
|
||
end
|
||
5000.times do
|
||
10000.times do
|
||
h.keys
|
||
end
|
benchmark/bm_hash_shift.rb | ||
---|---|---|
h[i] = nil
|
||
end
|
||
50000.times do
|
||
1000000.times do
|
||
k, v = h.shift
|
||
h[k] = v
|
||
end
|
benchmark/bm_hash_shift_u16.rb | ||
---|---|---|
h[i] = nil
|
||
end
|
||
300000.times do
|
||
1000000.times do
|
||
k, v = h.shift
|
||
h[k] = v
|
||
end
|
benchmark/bm_hash_shift_u24.rb | ||
---|---|---|
h[i] = nil
|
||
end
|
||
300000.times do
|
||
1000000.times do
|
||
k, v = h.shift
|
||
h[k] = v
|
||
end
|
benchmark/bm_hash_shift_u32.rb | ||
---|---|---|
h[i] = nil
|
||
end
|
||
300000.times do
|
||
1000000.times do
|
||
k, v = h.shift
|
||
h[k] = v
|
||
end
|
benchmark/bm_hash_to_proc.rb | ||
---|---|---|
h[i] = nil
|
||
end
|
||
5000.times do |i|
|
||
500000.times do |i|
|
||
[i].map(&h)
|
||
end
|
benchmark/bm_hash_values.rb | ||
---|---|---|
h[i] = nil
|
||
end
|
||
5000.times do
|
||
10000.times do
|
||
h.values
|
||
end
|
ext/-test-/st/foreach/foreach.c | ||
---|---|---|
if (c->nr == 0) {
|
||
st_data_t i;
|
||
if (!c->tbl->entries_packed) rb_bug("should be packed\n");
|
||
if (c->tbl->bins != NULL) rb_bug("should be packed\n");
|
||
/* force unpacking during iteration: */
|
||
for (i = 1; i < expect_size; i++)
|
||
st_add_direct(c->tbl, i, i);
|
||
if (c->tbl->entries_packed) rb_bug("should be unpacked\n");
|
||
if (c->tbl->bins == NULL) rb_bug("should be unpacked\n");
|
||
}
|
||
if (key != c->nr) {
|
||
... | ... | |
st_add_direct(tbl, 0, 0);
|
||
if (!tbl->entries_packed) rb_bug("should still be packed\n");
|
||
if (tbl->bins != NULL) rb_bug("should still be packed\n");
|
||
st_foreach_check(tbl, unp_fec_i, (st_data_t)&c, -1);
|
||
... | ... | |
(VALUE)c.nr, (VALUE)expect_size);
|
||
}
|
||
if (tbl->entries_packed) rb_bug("should be unpacked\n");
|
||
if (tbl->bins == NULL) rb_bug("should be unpacked\n");
|
||
st_free_table(tbl);
|
||
... | ... | |
st_add_direct(tbl, 0, 0);
|
||
if (!tbl->entries_packed) rb_bug("should still be packed\n");
|
||
if (tbl->bins != NULL) rb_bug("should still be packed\n");
|
||
st_foreach(tbl, unp_fe_i, (st_data_t)&c);
|
||
... | ... | |
(VALUE)c.nr, (VALUE)expect_size);
|
||
}
|
||
if (tbl->entries_packed) rb_bug("should be unpacked\n");
|
||
if (tbl->bins == NULL) rb_bug("should be unpacked\n");
|
||
st_free_table(tbl);
|
||
include/ruby/st.h | ||
---|---|---|
/* This is a public domain general purpose hash table package written by Peter Moore @ UCB. */
|
||
/* This is a public domain general purpose hash table package
|
||
originally written by Peter Moore @ UCB.
|
||
/* @(#) st.h 5.1 89/12/14 */
|
||
The hash table data strutures were redesigned and the package was
|
||
rewritten by Vladimir Makarov <vmakarov@redhat.com>. */
|
||
#ifndef RUBY_ST_H
|
||
#define RUBY_ST_H 1
|
||
... | ... | |
typedef struct st_table st_table;
|
||
typedef st_data_t st_index_t;
|
||
/* Maximal value of unsigned integer type st_index_t. */
|
||
#define MAX_ST_INDEX_VAL (~(st_index_t) 0)
|
||
|
||
typedef int st_compare_func(st_data_t, st_data_t);
|
||
typedef st_index_t st_hash_func(st_data_t);
|
||
... | ... | |
st_index_t (*hash)(ANYARGS /*st_data_t*/); /* st_hash_func* */
|
||
};
|
||
#define ST_INDEX_BITS (sizeof(st_index_t) * CHAR_BIT)
|
||
#if defined(HAVE_BUILTIN___BUILTIN_CHOOSE_EXPR) && defined(HAVE_BUILTIN___BUILTIN_TYPES_COMPATIBLE_P)
|
||
# define ST_DATA_COMPATIBLE_P(type) \
|
||
__builtin_choose_expr(__builtin_types_compatible_p(type, st_data_t), 1, 0)
|
||
... | ... | |
# define ST_DATA_COMPATIBLE_P(type) 0
|
||
#endif
|
||
typedef struct st_table_entry st_table_entry;
|
||
struct st_table_entry; /* defined in st.c */
|
||
struct st_table {
|
||
/* Cached features of the table -- see st.c for more details. */
|
||
unsigned char entry_power, bin_power, size_ind;
|
||
/* How many times the table was rebuilt. */
|
||
unsigned int rebuilds_num;
|
||
const struct st_hash_type *type;
|
||
st_index_t num_bins;
|
||
unsigned int entries_packed : 1;
|
||
#ifdef __GNUC__
|
||
/*
|
||
* C spec says,
|
||
* A bit-field shall have a type that is a qualified or unqualified
|
||
* version of _Bool, signed int, unsigned int, or some other
|
||
* implementation-defined type. It is implementation-defined whether
|
||
* atomic types are permitted.
|
||
* In short, long and long long bit-field are implementation-defined
|
||
* feature. Therefore we want to suppress a warning explicitly.
|
||
*/
|
||
__extension__
|
||
#endif
|
||
st_index_t num_entries : ST_INDEX_BITS - 1;
|
||
union {
|
||
struct {
|
||
struct st_table_entry **bins;
|
||
void *private_list_head[2];
|
||
} big;
|
||
struct {
|
||
struct st_packed_entry *entries;
|
||
st_index_t real_entries;
|
||
} packed;
|
||
} as;
|
||
/* Number of entries currently in the table. */
|
||
st_index_t num_entries;
|
||
/* Array of bins used for access by keys. */
|
||
st_index_t *bins;
|
||
/* Start and bound index of entries in array entries.
|
||
entries_starts and entries_bound are in interval
|
||
[0,allocated_entries]. */
|
||
st_index_t entries_start, entries_bound;
|
||
/* Array of size 2^entry_power. */
|
||
st_table_entry *entries;
|
||
};
|
||
#define st_is_member(table,key) st_lookup((table),(key),(st_data_t *)0)
|
||
... | ... | |
int st_update(st_table *table, st_data_t key, st_update_callback_func *func, st_data_t arg);
|
||
int st_foreach(st_table *, int (*)(ANYARGS), st_data_t);
|
||
int st_foreach_check(st_table *, int (*)(ANYARGS), st_data_t, st_data_t);
|
||
int st_reverse_foreach(st_table *, int (*)(ANYARGS), st_data_t);
|
||
st_index_t st_keys(st_table *table, st_data_t *keys, st_index_t size);
|
||
st_index_t st_keys_check(st_table *table, st_data_t *keys, st_index_t size, st_data_t never);
|
||
st_index_t st_values(st_table *table, st_data_t *values, st_index_t size);
|
||
st_index_t st_values_check(st_table *table, st_data_t *values, st_index_t size, st_data_t never);
|
||
void st_add_direct(st_table *, st_data_t, st_data_t);
|
||
void st_free_table(st_table *);
|
||
size_t st_memsize(const st_table *);
|
||
void st_cleanup_safe(st_table *, st_data_t);
|
||
void st_clear(st_table *);
|
||
st_table *st_copy(st_table *);
|
||
... | ... | |
int st_locale_insensitive_strncasecmp(const char *s1, const char *s2, size_t n);
|
||
#define st_strcasecmp st_locale_insensitive_strcasecmp
|
||
#define st_strncasecmp st_locale_insensitive_strncasecmp
|
||
size_t st_memsize(const st_table *);
|
||
st_index_t st_hash(const void *ptr, size_t len, st_index_t h);
|
||
st_index_t st_hash_uint32(st_index_t h, uint32_t i);
|
||
st_index_t st_hash_uint(st_index_t h, st_index_t i);
|
st.c | ||
---|---|---|
/* This is a public domain general purpose hash table package written by Peter Moore @ UCB. */
|
||
/* static char sccsid[] = "@(#) st.c 5.1 89/12/14 Crucible"; */
|
||
/* This is a public domain general purpose hash table package
|
||
originally written by Peter Moore @ UCB.
|
||
The hash table data structures were redesigned and the package was
|
||
rewritten by Vladimir Makarov <vmakarov@redhat.com>. */
|
||
/* The original package implemented classic bucket-based hash tables
|
||
with entries doubly linked for an access by their insertion order.
|
||
To decrease pointer chasing and as a consequence to improve a data
|
||
locality the current implementation is based on storing entries in
|
||
an array and using hash tables with open addressing. The current
|
||
entries are more compact in comparison with the original ones and
|
||
this also improves the data locality.
|
||
The hash table has two arrays called *bins* and *entries*.
|
||
bins:
|
||
-------
|
||
| | entries array:
|
||
|-------| --------------------------------
|
||
| index | | | entry: | | |
|
||
|-------| | | | | |
|
||
| ... | | ... | hash | ... | ... |
|
||
|-------| | | key | | |
|
||
| empty | | | record | | |
|
||
|-------| --------------------------------
|
||
| ... | ^ ^
|
||
|-------| |_ entries start |_ entries bound
|
||
|deleted|
|
||
-------
|
||
o The entry array contains table entries in the same order as they
|
||
were inserted.
|
||
When the first entry is deleted, a variable containing index of
|
||
the current first entry (*entries start*) is changed. In all
|
||
other cases of the deletion, we just mark the entry as deleted by
|
||
using a reserved hash value.
|
||
Such organization of the entry storage makes operations of the
|
||
table shift and the entries traversal very fast.
|
||
o The bins provide access to the entries by their keys. The
|
||
key hash is mapped to a bin containing *index* of the
|
||
corresponding entry in the entry array.
|
||
The bin array size is always power of two, it makes mapping very
|
||
fast by using the corresponding lower bits of the hash.
|
||
Generally it is not a good idea to ignore some part of the hash.
|
||
But alternative approach is worse. For example, we could use a
|
||
modulo operation for mapping and a prime number for the size of
|
||
the bin array. Unfortunately, the modulo operation for big
|
||
64-bit numbers are extremely slow (it takes more than 100 cycles
|
||
on modern Intel CPUs).
|
||
Still other bits of the hash value are used when the mapping
|
||
results in a collision. In this case we use a secondary hash
|
||
value which is a result of a function of the collision bin
|
||
index and the original hash value. The function choice
|
||
guarantees that we can traverse all bins and finally find the
|
||
corresponding bin as after several iterations the function
|
||
becomes a full cycle linear congruential generator because it
|
||
satisfies requirements of the Hull-Dobell theorem.
|
||
When an entry is removed from the table besides marking the
|
||
hash in the corresponding entry described above, we also mark
|
||
the bin by a special value in order to find entries which had
|
||
a collision with the removed entries.
|
||
There are two reserved values for the bins. One denotes an
|
||
empty bin, another one denotes a bin for a deleted entry.
|
||
o The length of the bin array is at least two times more than the
|
||
entry array length. This keeps the table load factor healthy.
|
||
The trigger of rebuilding the table is always a case when we can
|
||
not insert an entry anymore at the entries bound. We could
|
||
change the entries bound too in case of deletion but than we need
|
||
a special code to count bins with corresponding deleted entries
|
||
and reset the bin values when there are too many bins
|
||
corresponding deleted entries
|
||
Table rebuilding is done by creation of a new entry array and
|
||
bins of an appropriate size. We also try to reuse the arrays
|
||
in some cases by compacting the array and removing deleted
|
||
entries.
|
||
o To save memory very small tables have no allocated arrays
|
||
bins. We use a linear search for an access by a key.
|
||
o To save more memory we use 8-, 16-, 32- and 64- bit indexes in
|
||
bins depending on the current hash table size.
|
||
This implementation speeds up the Ruby hash table benchmarks in
|
||
average by more 40% on Intel Haswell CPU.
|
||
*/
|
||
#ifdef NOT_RUBY
|
||
#include "regint.h"
|
||
... | ... | |
#include <stdlib.h>
|
||
#endif
|
||
#include <string.h>
|
||
#include "ccan/list/list.h"
|
||
#include <assert.h>
|
||
#ifdef __GNUC__
|
||
#define PREFETCH(addr, write_p) __builtin_prefetch(addr, write_p)
|
||
#define EXPECT(expr, val) __builtin_expect(expr, val)
|
||
#define ATTRIBUTE_UNUSED __attribute__((unused))
|
||
#else
|
||
#define PREFETCH(addr, write_p)
|
||
#define EXPECT(expr, val) (expr)
|
||
#define ATTRIBUTE_UNUSED
|
||
#endif
|
||
#ifdef ST_DEBUG
|
||
#define st_assert(cond) assert(cond)
|
||
#else
|
||
#define st_assert(cond) ((void)(0 && (cond)))
|
||
#endif
|
||
typedef struct st_table_entry st_table_entry;
|
||
/* The type of hashes. */
|
||
typedef st_index_t st_hash_t;
|
||
struct st_table_entry {
|
||
st_index_t hash;
|
||
st_hash_t hash;
|
||
st_data_t key;
|
||
st_data_t record;
|
||
st_table_entry *next;
|
||
struct list_node olist;
|
||
};
|
||
typedef struct st_packed_entry {
|
||
st_index_t hash;
|
||
st_data_t key, val;
|
||
} st_packed_entry;
|
||
#ifndef STATIC_ASSERT
|
||
#define STATIC_ASSERT(name, expr) typedef int static_assert_##name##_check[(expr) ? 1 : -1]
|
||
#endif
|
||
#define ST_DEFAULT_MAX_DENSITY 5
|
||
#define ST_DEFAULT_INIT_TABLE_SIZE 16
|
||
#define ST_DEFAULT_PACKED_TABLE_SIZE 18
|
||
#define PACKED_UNIT (int)(sizeof(st_packed_entry) / sizeof(st_table_entry*))
|
||
#define MAX_PACKED_HASH (int)(ST_DEFAULT_PACKED_TABLE_SIZE * sizeof(st_table_entry*) / sizeof(st_packed_entry))
|
||
STATIC_ASSERT(st_packed_entry, sizeof(st_packed_entry) == sizeof(st_table_entry*[PACKED_UNIT]));
|
||
STATIC_ASSERT(st_packed_bins, sizeof(st_packed_entry[MAX_PACKED_HASH]) <= sizeof(st_table_entry*[ST_DEFAULT_PACKED_TABLE_SIZE]));
|
||
/*
|
||
* DEFAULT_MAX_DENSITY is the default for the largest we allow the
|
||
* average number of items per bin before increasing the number of
|
||
* bins
|
||
*
|
||
* DEFAULT_INIT_TABLE_SIZE is the default for the number of bins
|
||
* allocated initially
|
||
*
|
||
*/
|
||
#define type_numhash st_hashtype_num
|
||
const struct st_hash_type st_hashtype_num = {
|
||
st_numcmp,
|
||
... | ... | |
strcasehash,
|
||
};
|
||
static void rehash(st_table *);
|
||
/* Value used to catch uninitialized entries/bins during debugging.
|
||
There is a possibility for a false alarm, but its probability is
|
||
extremely small. */
|
||
#define ST_INIT_VAL 0xafafafafafafafaf
|
||
#define ST_INIT_VAL_BYTE 0xafa
|
||
#ifdef RUBY
|
||
#undef malloc
|
||
#undef realloc
|
||
#undef calloc
|
||
#undef free
|
||
#define malloc xmalloc
|
||
#define calloc xcalloc
|
||
#define realloc xrealloc
|
||
#define free(x) xfree(x)
|
||
#endif
|
||
#define EQUAL(table,x,ent) ((x)==(ent)->key || (*(table)->type->compare)((x),(ent)->key) == 0)
|
||
#define do_hash(key,table) (st_index_t)(*(table)->type->hash)((key))
|
||
#define hash_pos(h,n) ((h) & (n - 1))
|
||
/* preparation for possible allocation improvements */
|
||
#define st_alloc_entry() (st_table_entry *)malloc(sizeof(st_table_entry))
|
||
#define st_free_entry(entry) free(entry)
|
||
#define st_alloc_table() (st_table *)malloc(sizeof(st_table))
|
||
#define st_dealloc_table(table) free(table)
|
||
#define st_alloc_bins(size) (st_table_entry **)calloc(size, sizeof(st_table_entry *))
|
||
#define st_free_bins(bins, size) free(bins)
|
||
static inline st_table_entry**
|
||
st_realloc_bins(st_table_entry **bins, st_index_t newsize, st_index_t oldsize)
|
||
{
|
||
bins = (st_table_entry **)realloc(bins, newsize * sizeof(st_table_entry *));
|
||
MEMZERO(bins, st_table_entry*, newsize);
|
||
return bins;
|
||
}
|
||
/* Shortcut */
|
||
#define bins as.big.bins
|
||
#define real_entries as.packed.real_entries
|
||
/* preparation for possible packing improvements */
|
||
#define PACKED_BINS(table) ((table)->as.packed.entries)
|
||
#define PACKED_ENT(table, i) PACKED_BINS(table)[i]
|
||
#define PKEY(table, i) PACKED_ENT((table), (i)).key
|
||
#define PVAL(table, i) PACKED_ENT((table), (i)).val
|
||
#define PHASH(table, i) PACKED_ENT((table), (i)).hash
|
||
#define PKEY_SET(table, i, v) (PKEY((table), (i)) = (v))
|
||
#define PVAL_SET(table, i, v) (PVAL((table), (i)) = (v))
|
||
#define PHASH_SET(table, i, v) (PHASH((table), (i)) = (v))
|
||
/* this function depends much on packed layout, so that it placed here */
|
||
static inline void
|
||
remove_packed_entry(st_table *table, st_index_t i)
|
||
{
|
||
table->real_entries--;
|
||
table->num_entries--;
|
||
if (i < table->real_entries) {
|
||
MEMMOVE(&PACKED_ENT(table, i), &PACKED_ENT(table, i+1),
|
||
st_packed_entry, table->real_entries - i);
|
||
}
|
||
}
|
||
#define malloc ruby_xmalloc
|
||
#define calloc ruby_xcalloc
|
||
#define realloc ruby_xrealloc
|
||
#define free ruby_xfree
|
||
#endif
|
||
static inline void
|
||
remove_safe_packed_entry(st_table *table, st_index_t i, st_data_t never)
|
||
{
|
||
table->num_entries--;
|
||
PKEY_SET(table, i, never);
|
||
PVAL_SET(table, i, never);
|
||
PHASH_SET(table, i, 0);
|
||
}
|
||
#define EQUAL(tab,x,y) ((x) == (y) || (*(tab)->type->compare)((x),(y)) == 0)
|
||
#define PTR_EQUAL(tab, ptr, hash_val, key) \
|
||
((ptr)->hash == (hash_val) && EQUAL((tab), (key), (ptr)->key))
|
||
/* Features of a table. */
|
||
struct st_features {
|
||
/* Power of 2 used for number of allocated entries. */
|
||
unsigned char entry_power;
|
||
/* Power of 2 used for number of allocated bins. Depending on the
|
||
table size, the number of bins is 2-4 times more than the
|
||
number of entries. */
|
||
unsigned char bin_power;
|
||
/* Enumeration of sizes of bins (8-bit, 16-bit etc). */
|
||
unsigned char size_ind;
|
||
/* Bins are packed in words of type st_index_t. The following is
|
||
a size of bins counted by words. */
|
||
st_index_t bins_words;
|
||
};
|
||
static st_index_t
|
||
next_pow2(st_index_t x)
|
||
{
|
||
x |= x >> 1;
|
||
x |= x >> 2;
|
||
x |= x >> 4;
|
||
x |= x >> 8;
|
||
x |= x >> 16;
|
||
/* Features of all possible size tables. */
|
||
#if SIZEOF_ST_INDEX_T == 8
|
||
x |= x >> 32;
|
||
#define MAX_POWER2 62
|
||
struct st_features features[] = {
|
||
{0, 2, 0, 0x0},
|
||
{1, 3, 0, 0x1},
|
||
{2, 4, 0, 0x2},
|
||
{3, 5, 0, 0x4},
|
||
{4, 6, 0, 0x8},
|
||
{5, 7, 0, 0x10},
|
||
{6, 8, 0, 0x20},
|
||
{7, 9, 0, 0x40},
|
||
{8, 10, 1, 0x100},
|
||
{9, 11, 1, 0x200},
|
||
{10, 12, 1, 0x400},
|
||
{11, 13, 1, 0x800},
|
||
{12, 14, 1, 0x1000},
|
||
{13, 15, 1, 0x2000},
|
||
{14, 16, 1, 0x4000},
|
||
{15, 17, 1, 0x8000},
|
||
{16, 18, 2, 0x20000},
|
||
{17, 19, 2, 0x40000},
|
||
{18, 20, 2, 0x80000},
|
||
{19, 21, 2, 0x100000},
|
||
{20, 22, 2, 0x200000},
|
||
{21, 23, 2, 0x400000},
|
||
{22, 24, 2, 0x800000},
|
||
{23, 25, 2, 0x1000000},
|
||
{24, 26, 2, 0x2000000},
|
||
{25, 27, 2, 0x4000000},
|
||
{26, 28, 2, 0x8000000},
|
||
{27, 29, 2, 0x10000000},
|
||
{28, 30, 2, 0x20000000},
|
||
{29, 31, 2, 0x40000000},
|
||
{30, 32, 2, 0x80000000},
|
||
{31, 33, 2, 0x100000000},
|
||
{32, 33, 3, 0x200000000},
|
||
{33, 34, 3, 0x400000000},
|
||
{34, 35, 3, 0x800000000},
|
||
{35, 36, 3, 0x1000000000},
|
||
{36, 37, 3, 0x2000000000},
|
||
{37, 38, 3, 0x4000000000},
|
||
{38, 39, 3, 0x8000000000},
|
||
{39, 40, 3, 0x10000000000},
|
||
{40, 41, 3, 0x20000000000},
|
||
{41, 42, 3, 0x40000000000},
|
||
{42, 43, 3, 0x80000000000},
|
||
{43, 44, 3, 0x100000000000},
|
||
{44, 45, 3, 0x200000000000},
|
||
{45, 46, 3, 0x400000000000},
|
||
{46, 47, 3, 0x800000000000},
|
||
{47, 48, 3, 0x1000000000000},
|
||
{48, 49, 3, 0x2000000000000},
|
||
{49, 50, 3, 0x4000000000000},
|
||
{50, 51, 3, 0x8000000000000},
|
||
{51, 52, 3, 0x10000000000000},
|
||
{52, 53, 3, 0x20000000000000},
|
||
{53, 54, 3, 0x40000000000000},
|
||
{54, 55, 3, 0x80000000000000},
|
||
{55, 56, 3, 0x100000000000000},
|
||
{56, 57, 3, 0x200000000000000},
|
||
{57, 58, 3, 0x400000000000000},
|
||
{58, 59, 3, 0x800000000000000},
|
||
{59, 60, 3, 0x1000000000000000},
|
||
{60, 61, 3, 0x2000000000000000},
|
||
{61, 62, 3, 0x4000000000000000},
|
||
{62, 63, 3, 0x8000000000000000},
|
||
};
|
||
#else
|
||
#define MAX_POWER2 30
|
||
struct st_features features[] = {
|
||
{0, 2, 0, 0x1},
|
||
{1, 3, 0, 0x2},
|
||
{2, 4, 0, 0x4},
|
||
{3, 5, 0, 0x8},
|
||
{4, 6, 0, 0x10},
|
||
{5, 7, 0, 0x20},
|
||
{6, 8, 0, 0x40},
|
||
{7, 9, 0, 0x80},
|
||
{8, 10, 1, 0x200},
|
||
{9, 11, 1, 0x400},
|
||
{10, 12, 1, 0x800},
|
||
{11, 13, 1, 0x1000},
|
||
{12, 14, 1, 0x2000},
|
||
{13, 15, 1, 0x4000},
|
||
{14, 16, 1, 0x8000},
|
||
{15, 17, 1, 0x10000},
|
||
{16, 17, 2, 0x20000},
|
||
{17, 18, 2, 0x40000},
|
||
{18, 19, 2, 0x80000},
|
||
{19, 20, 2, 0x100000},
|
||
{20, 21, 2, 0x200000},
|
||
{21, 22, 2, 0x400000},
|
||
{22, 23, 2, 0x800000},
|
||
{23, 24, 2, 0x1000000},
|
||
{24, 25, 2, 0x2000000},
|
||
{25, 26, 2, 0x4000000},
|
||
{26, 27, 2, 0x8000000},
|
||
{27, 28, 2, 0x10000000},
|
||
{28, 29, 2, 0x20000000},
|
||
{29, 30, 2, 0x40000000},
|
||
{30, 31, 2, 0x80000000},
|
||
};
|
||
#endif
|
||
/* The reserved hash value and its substitution. */
|
||
#define RESERVED_HASH_VAL (~(st_hash_t) 0)
|
||
#define RESERVED_HASH_SUBSTITUTION_VAL ((st_hash_t) 0)
|
||
/* Return hash value of KEY for table TAB. */
|
||
static inline st_hash_t
|
||
do_hash(st_data_t key, st_table *tab) {
|
||
st_index_t h = (st_index_t)(tab->type->hash)(key);
|
||
#if SIZEOF_INT == SIZEOF_VOIDP
|
||
st_hash_t hash = h;
|
||
#else
|
||
st_hash_t hash = h;
|
||
#endif
|
||
return x + 1;
|
||
/* RESERVED_HASH_VAL is used for a deleted entry. Map it into
|
||
another value. Such mapping should be extremely rare. */
|
||
return hash == RESERVED_HASH_VAL ? RESERVED_HASH_SUBSTITUTION_VAL : hash;
|
||
}
|
||
static st_index_t
|
||
new_size(st_index_t size)
|
||
{
|
||
st_index_t n;
|
||
/* Power of 2 defining the minimal number of allocated entries. */
|
||
#define MINIMAL_POWER2 3
|
||
#if MINIMAL_POWER2 < 2
|
||
#error "MINIMAL_POWER2 should be >= 2"
|
||
#endif
|
||
/* If the power2 of the allocated `entries` is less than the following
|
||
value, don't allocate bins and use a linear search. */
|
||
#define MAX_POWER2_FOR_TABLES_WITHOUT_BINS 3
|
||
if (size && (size & ~(size - 1)) == size) /* already a power-of-two? */
|
||
return size;
|
||
/* Return smallest n >= MINIMAL_POWER2 such 2^n > SIZE. */
|
||
static int
|
||
get_power2(st_index_t size) {
|
||
unsigned int n;
|
||
n = next_pow2(size);
|
||
if (n > size)
|
||
return n;
|
||
for (n = 0; size != 0; n++)
|
||
size >>= 1;
|
||
if (n <= MAX_POWER2)
|
||
return n < MINIMAL_POWER2 ? MINIMAL_POWER2 : n;
|
||
#ifndef NOT_RUBY
|
||
/* Ran out of the table entries */
|
||
rb_raise(rb_eRuntimeError, "st_table too big");
|
||
#endif
|
||
return -1; /* should raise exception */
|
||
/* should raise exception */
|
||
return -1;
|
||
}
|
||
/* Return value of N-th bin in array BINS of table with bins size
|
||
index S. */
|
||
static inline st_index_t
|
||
get_bin(st_index_t *bins, int s, st_index_t n) {
|
||
return (s == 0 ? ((unsigned char *) bins)[n]
|
||
: s == 1 ? ((unsigned short *) bins)[n]
|
||
: s == 2 ? ((unsigned int *) bins)[n]
|
||
: ((st_index_t *) bins)[n]);
|
||
}
|
||
/* Set up N-th bin in array BINS of table with bins size index S to
|
||
value V. */
|
||
static inline void
|
||
set_bin(st_index_t *bins, int s, st_index_t n, st_index_t v) {
|
||
if (s == 0) ((unsigned char *) bins)[n] = v;
|
||
else if (s == 1) ((unsigned short *) bins)[n] = v;
|
||
else if (s == 2) ((unsigned int *) bins)[n] = v;
|
||
else ((st_index_t *) bins)[n] = v;
|
||
}
|
||
/* These macros define reserved values for empty table bin and table
|
||
bin which contains a deleted entry. We will never use such values
|
||
for an entry index in bins. */
|
||
#define EMPTY_BIN 0
|
||
#define DELETED_BIN 1
|
||
/* Base of a real entry index in the bins. */
|
||
#define ENTRY_BASE 2
|
||
/* Mark I-th bin of table TAB as empty, in other words not
|
||
corresponding to any entry. */
|
||
#define MARK_BIN_EMPTY(tab, i) (set_bin((tab)->bins, get_size_ind(tab), i, EMPTY_BIN))
|
||
/* Values used for not found entry and bin with given
|
||
characteristics. */
|
||
#define UNDEFINED_ENTRY_IND (~(st_index_t) 0)
|
||
#define UNDEFINED_BIN_IND (~(st_index_t) 0)
|
||
/* 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. */
|
||
#define MARK_BIN_DELETED(tab, i) \
|
||
do { \
|
||
st_assert(i != UNDEFINED_BIN_IND); \
|
||
st_assert(! IND_EMPTY_OR_DELETED_BIN_P(tab, i)); \
|
||
set_bin((tab)->bins, get_size_ind(tab), i, DELETED_BIN); \
|
||
} while (0)
|
||
/* Macros to check that value B is used empty bins and bins
|
||
corresponding deleted entries. */
|
||
#define EMPTY_BIN_P(b) ((b) == EMPTY_BIN)
|
||
#define DELETED_BIN_P(b) ((b) == DELETED_BIN)
|
||
#define EMPTY_OR_DELETED_BIN_P(b) ((b) <= DELETED_BIN)
|
||
/* Macros to check empty bins and bins corresponding to deleted
|
||
entries. Bins are given by their index I in table TAB. */
|
||
#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)))
|
||
/* 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)
|
||
/* Return bin size index of table TAB. */
|
||
static inline st_index_t
|
||
get_size_ind(const st_table *tab) {
|
||
return tab->size_ind;
|
||
}
|
||
/* Return the number of allocated bins of table TAB. */
|
||
static inline st_index_t
|
||
get_bins_num(const st_table *tab) {
|
||
return 1<<tab->bin_power;
|
||
}
|
||
/* Return mask for a bin index in table TAB. */
|
||
static inline st_index_t
|
||
bins_mask(const st_table *tab) {
|
||
return get_bins_num(tab) - 1;
|
||
}
|
||
/* Return the index of table TAB bin corresponding to
|
||
HASH_VALUE. */
|
||
static inline st_index_t
|
||
hash_bin(st_hash_t hash_value, st_table *tab) {
|
||
return hash_value & bins_mask(tab);
|
||
}
|
||
/* Return the number of allocated entries of table TAB. */
|
||
static inline st_index_t
|
||
get_allocated_entries(const st_table *tab) {
|
||
return 1<<tab->entry_power;
|
||
}
|
||
/* 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);
|
||
}
|
||
/* Mark all bins of table TAB as empty. */
|
||
static void
|
||
initialize_bins(st_table *tab) {
|
||
memset(tab->bins, 0, bins_size(tab));
|
||
}
|
||
/* Make table TAB empty. */
|
||
static void
|
||
make_tab_empty(st_table *tab)
|
||
{
|
||
tab->num_entries = 0;
|
||
tab->entries_start = tab->entries_bound = 0;
|
||
if (tab->bins != NULL)
|
||
initialize_bins(tab);
|
||
}
|
||
#ifdef ST_DEBUG
|
||
/* Check the table T consistency. It can be extremely slow. So use
|
||
it only for debugging. */
|
||
static void
|
||
st_check(st_table *tab) {
|
||
st_index_t d, e, i, n, p;
|
||
for (p = get_allocated_entries(tab), i = 0; p > 1; i++, p>>=1)
|
||
;
|
||
p = i;
|
||
assert(p >= MINIMAL_POWER2);
|
||
assert(tab->entries_bound <= get_allocated_entries(tab)
|
||
&& tab->entries_start <= tab->entries_bound);
|
||
n = 0;
|
||
return;
|
||
if (tab->entries_bound != 0)
|
||
for (i = tab->entries_start; i < tab->entries_bound; i++) {
|
||
assert(tab->entries[i].hash != (st_hash_t) ST_INIT_VAL
|
||
&& tab->entries[i].key != ST_INIT_VAL
|
||
&& tab->entries[i].record != ST_INIT_VAL);
|
||
if (! DELETED_ENTRY_P(&tab->entries[i]))
|
||
n++;
|
||
}
|
||
assert(n == tab->num_entries);
|
||
if (tab->bins == NULL)
|
||
assert(p <= MAX_POWER2_FOR_TABLES_WITHOUT_BINS);
|
||
else {
|
||
assert(p > MAX_POWER2_FOR_TABLES_WITHOUT_BINS);
|
||
for (n = d = i = 0; i < get_bins_num(tab); i++) {
|
||
assert(get_bin(tab->bins, tab->size_ind, i) != ST_INIT_VAL);
|
||
if (IND_DELETED_BIN_P(tab, i)) {
|
||
d++;
|
||
continue;
|
||
}
|
||
else if (IND_EMPTY_BIN_P(tab, i))
|
||
continue;
|
||
n++;
|
||
e = get_bin(tab->bins, tab->size_ind, i) - ENTRY_BASE;
|
||
assert(tab->entries_start <= e && e < tab->entries_bound);
|
||
assert(! DELETED_ENTRY_P(&tab->entries[e]));
|
||
assert(tab->entries[e].hash != (st_hash_t) ST_INIT_VAL
|
||
&& tab->entries[e].key != ST_INIT_VAL
|
||
&& tab->entries[e].record != ST_INIT_VAL);
|
||
}
|
||
assert(n == tab->num_entries);
|
||
assert(n + d < get_bins_num(tab));
|
||
}
|
||
}
|
||
#endif
|
||
#ifdef HASH_LOG
|
||
#ifdef HAVE_UNISTD_H
|
||
... | ... | |
static struct {
|
||
int all, total, num, str, strcase;
|
||
} collision;
|
||
/* Flag switching off output of package statistics at the end of
|
||
program. */
|
||
static int init_st = 0;
|
||
/* Output overall number of table searches and collisions into a
|
||
temporary file. */
|
||
static void
|
||
stat_col(void)
|
||
{
|
||
... | ... | |
if (!collision.total) return;
|
||
f = fopen((snprintf(fname, sizeof(fname), "/tmp/col%ld", (long)getpid()), fname), "w");
|
||
fprintf(f, "collision: %d / %d (%6.2f)\n", collision.all, collision.total,
|
||
((double)collision.all / (collision.total)) * 100);
|
||
((double)collision.all / (collision.total)) * 100);
|
||
fprintf(f, "num: %d, str: %d, strcase: %d\n", collision.num, collision.str, collision.strcase);
|
||
fclose(f);
|
||
}
|
||
#endif
|
||
static struct list_head *
|
||
st_head(const st_table *tbl)
|
||
{
|
||
uintptr_t addr = (uintptr_t)&tbl->as.big.private_list_head;
|
||
return (struct list_head *)addr;
|
||
}
|
||
st_table*
|
||
st_init_table_with_size(const struct st_hash_type *type, st_index_t size)
|
||
{
|
||
st_table *tbl;
|
||
/* Create and return table with TYPE which can hold at least SIZE
|
||
entries. The real number of entries which the table can hold is
|
||
the nearest power of two for SIZE. */
|
||
st_table *
|
||
st_init_table_with_size(const struct st_hash_type *type, st_index_t size) {
|
||
st_table *tab;
|
||
int n;
|
||
|
||
#ifdef HASH_LOG
|
||
# if HASH_LOG+0 < 0
|
||
#if HASH_LOG+0 < 0
|
||
{
|
||
const char *e = getenv("ST_HASH_LOG");
|
||
if (!e || !*e) init_st = 1;
|
||
const char *e = getenv("ST_HASH_LOG");
|
||
if (!e || !*e) init_st = 1;
|
||
}
|
||
# endif
|
||
#endif
|
||
if (init_st == 0) {
|
||
init_st = 1;
|
||
atexit(stat_col);
|
||
init_st = 1;
|
||
atexit(stat_col);
|
||
}
|
||
#endif
|
||
tbl = st_alloc_table();
|
||
tbl->type = type;
|
||
tbl->num_entries = 0;
|
||
tbl->entries_packed = size <= MAX_PACKED_HASH;
|
||
if (tbl->entries_packed) {
|
||
size = ST_DEFAULT_PACKED_TABLE_SIZE;
|
||
tbl->real_entries = 0;
|
||
}
|
||
else {
|
||
size = new_size(size); /* round up to power-of-two */
|
||
list_head_init(st_head(tbl));
|
||
}
|
||
tbl->num_bins = size;
|
||
tbl->bins = st_alloc_bins(size);
|
||
return tbl;
|
||
|
||
n = get_power2(size);
|
||
tab = (st_table *) malloc(sizeof (st_table));
|
||
tab->type = type;
|
||
tab->entry_power = n;
|
||
tab->bin_power = features[n].bin_power;
|
||
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->entries = (st_table_entry *) malloc(get_allocated_entries(tab)
|
||
* sizeof(st_table_entry));
|
||
#ifdef ST_DEBUG
|
||
memset(tab->entries, ST_INIT_VAL_BYTE,
|
||
get_allocated_entries(tab) * sizeof(st_table_entry));
|
||
if (tab->bins != NULL)
|
||
memset(tab->bins, ST_INIT_VAL_BYTE, bins_size(tab));
|
||
#endif
|
||
make_tab_empty(tab);
|
||
tab->rebuilds_num = 0;
|
||
#ifdef ST_DEBUG
|
||
st_check(tab);
|
||
#endif
|
||
return tab;
|
||
}
|
||
st_table*
|
||
st_init_table(const struct st_hash_type *type)
|
||
{
|
||
/* Create and return table with TYPE which can hold a minimal number
|
||
of entries (see comments for get_power2). */
|
||
st_table *
|
||
st_init_table(const struct st_hash_type *type) {
|
||
return st_init_table_with_size(type, 0);
|
||
}
|
||
st_table*
|
||
st_init_numtable(void)
|
||
{
|
||
/* Create and return table which can hold a minimal number of
|
||
numbers. */
|
||
st_table *
|
||
st_init_numtable(void) {
|
||
return st_init_table(&type_numhash);
|
||
}
|
||
st_table*
|
||
st_init_numtable_with_size(st_index_t size)
|
||
{
|
||
/* Create and return table which can hold SIZE numbers. */
|
||
st_table *
|
||
st_init_numtable_with_size(st_index_t size) {
|
||
return st_init_table_with_size(&type_numhash, size);
|
||
}
|
||
st_table*
|
||
st_init_strtable(void)
|
||
{
|
||
/* Create and return table which can hold a minimal number of
|
||
strings. */
|
||
st_table *
|
||
st_init_strtable(void) {
|
||
return st_init_table(&type_strhash);
|
||
}
|
||
st_table*
|
||
st_init_strtable_with_size(st_index_t size)
|
||
{
|
||
/* Create and return table which can hold SIZE strings. */
|
||
st_table *
|
||
st_init_strtable_with_size(st_index_t size) {
|
||
return st_init_table_with_size(&type_strhash, size);
|
||
}
|
||
st_table*
|
||
st_init_strcasetable(void)
|
||
{
|
||
/* Create and return table which can hold a minimal number of strings
|
||
whose character case is ignored. */
|
||
st_table *
|
||
st_init_strcasetable(void) {
|
||
return st_init_table(&type_strcasehash);
|
||
}
|
||
st_table*
|
||
st_init_strcasetable_with_size(st_index_t size)
|
||
{
|
||
/* Create and return table which can hold SIZE strings whose character
|
||
case is ignored. */
|
||
st_table *
|
||
st_init_strcasetable_with_size(st_index_t size) {
|
||
return st_init_table_with_size(&type_strcasehash, size);
|
||
}
|
||
/* Make table TAB empty. */
|
||
void
|
||
st_clear(st_table *table)
|
||
{
|
||
register st_table_entry *ptr = 0, *next;
|
||
if (table->entries_packed) {
|
||
table->num_entries = 0;
|
||
table->real_entries = 0;
|
||
return;
|
||
}
|
||
list_for_each_safe(st_head(table), ptr, next, olist) {
|
||
/* list_del is not needed */
|
||
st_free_entry(ptr);
|
||
}
|
||
table->num_entries = 0;
|
||
MEMZERO(table->bins, st_table_entry*, table->num_bins);
|
||
list_head_init(st_head(table));
|
||
st_clear(st_table *tab) {
|
||
make_tab_empty(tab);
|
||
tab->rebuilds_num++;
|
||
#ifdef ST_DEBUG
|
||
st_check(tab);
|
||
#endif
|
||
}
|
||
/* Free table TAB space. */
|
||
void
|
||
st_free_table(st_table *table)
|
||
{
|
||
st_clear(table);
|
||
st_free_bins(table->bins, table->num_bins);
|
||
st_dealloc_table(table);
|
||
st_free_table(st_table *tab) {
|
||
if (tab->bins != NULL)
|
||
free(tab->bins);
|
||
free(tab->entries);
|
||
free(tab);
|
||
}
|
||
/* Return byte size of memory allocted for table TAB. */
|
||
size_t
|
||
st_memsize(const st_table *table)
|
||
{
|
||
if (table->entries_packed) {
|
||
return table->num_bins * sizeof (void *) + sizeof(st_table);
|
||
}
|
||
else {
|
||
return table->num_entries * sizeof(struct st_table_entry) + table->num_bins * sizeof (void *) + sizeof(st_table);
|
||
}
|
||
st_memsize(const st_table *tab) {
|
||
return(sizeof(st_table)
|
||
+ (tab->bins == NULL ? 0 : bins_size(tab))
|
||
+ get_allocated_entries(tab) * sizeof(st_table_entry));
|
||
}
|
||
#define PTR_NOT_EQUAL(table, ptr, hash_val, key) \
|
||
((ptr) != 0 && ((ptr)->hash != (hash_val) || !EQUAL((table), (key), (ptr))))
|
||
static st_index_t
|
||
find_table_entry_ind(st_table *tab, st_hash_t hash_value, st_data_t key);
|
||
static st_index_t
|
||
find_table_bin_ind(st_table *tab, st_hash_t hash_value, st_data_t key);
|
||
static st_index_t
|
||
find_table_bin_ind_direct(st_table *table, st_hash_t hash_value, st_data_t key);
|
||
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);
|
||
#ifdef HASH_LOG
|
||
static void
|
||
count_collision(const struct st_hash_type *type)
|
||
{
|
||
count_collision(const struct st_hash_type *type) {
|
||
collision.all++;
|
||
if (type == &type_numhash) {
|
||
collision.num++;
|
||
collision.num++;
|
||
}
|
||
else if (type == &type_strhash) {
|
||
collision.strcase++;
|
||
collision.strcase++;
|
||
}
|
||
else if (type == &type_strcasehash) {
|
||
collision.str++;
|
||
collision.str++;
|
||
}
|
||
}
|
||
#define COLLISION (collision_check ? count_collision(table->type) : (void)0)
|
||
#define FOUND_ENTRY (collision_check ? collision.total++ : (void)0)
|
||
#define COLLISION (collision_check ? count_collision(tab->type) : (void)0)
|
||
#define FOUND_BIN (collision_check ? collision.total++ : (void)0)
|
||
#define collision_check 0
|
||
#else
|
||
#define COLLISION
|
||
#define FOUND_ENTRY
|
||
#define FOUND_BIN
|
||
#endif
|
||
#define FIND_ENTRY(table, ptr, hash_val, bin_pos) \
|
||
((ptr) = find_entry((table), key, (hash_val), ((bin_pos) = hash_pos(hash_val, (table)->num_bins))))
|
||
/* If the number of entries in the table is at least REBUILD_THRESHOLD
|
||
times less than the entry array length, decrease the table
|
||
size. */
|
||
#define REBUILD_THRESHOLD 4
|
||
static st_table_entry *
|
||
find_entry(const st_table *table, st_data_t key, st_index_t hash_val,
|
||
st_index_t bin_pos)
|
||
{
|
||
register st_table_entry *ptr = table->bins[bin_pos];
|
||
FOUND_ENTRY;
|
||
if (PTR_NOT_EQUAL(table, ptr, hash_val, key)) {
|
||
COLLISION;
|
||
while (PTR_NOT_EQUAL(table, ptr->next, hash_val, key)) {
|
||
ptr = ptr->next;
|
||
}
|
||
ptr = ptr->next;
|
||
}
|
||
return ptr;
|
||
}
|
||
#if REBUILD_THRESHOLD < 2
|
||
#error "REBUILD_THRESHOLD should be >= 2"
|
||
#endif
|
||
static inline st_index_t
|
||
find_packed_index_from(const st_table *table, st_index_t hash_val,
|
||
st_data_t key, st_index_t i)
|
||
{
|
||
while (i < table->real_entries &&
|
||
(PHASH(table, i) != hash_val || !EQUAL(table, key, &PACKED_ENT(table, i)))) {
|
||
i++;
|
||
/* Rebuild table TAB. Rebuilding removes all deleted bins and entries
|
||
and can change size of the table entries and bins arrays.
|
||
Rebuilding is implemented by creation of a new table or by
|
||
compaction of the existing one. */
|
||
static void
|
||
rebuild_table(st_table *tab) {
|
||
st_index_t i, ni, bound, size_ind;
|
||
st_table *new_tab;
|
||
st_table_entry *entries, *new_entries;
|
||
st_table_entry *curr_entry_ptr;
|
||
st_index_t *bins;
|
||
st_index_t bin_ind;
|
||
|
||
st_assert(tab != NULL);
|
||
bound = tab->entries_bound;
|
||
entries = tab->entries;
|
||
if ((2 * tab->num_entries <= get_allocated_entries(tab)
|
||
&& REBUILD_THRESHOLD * tab->num_entries > get_allocated_entries(tab))
|
||
|| tab->num_entries < (1 << MINIMAL_POWER2)) {
|
||
/* Compaction: */
|
||
tab->num_entries = 0;
|
||
if (tab->bins != NULL)
|
||
initialize_bins(tab);
|
||
new_tab = tab;
|
||
new_entries = entries;
|
||
}
|
||
return i;
|
||
}
|
||
static inline st_index_t
|
||
find_packed_index(const st_table *table, st_index_t hash_val, st_data_t key)
|
||
{
|
||
return find_packed_index_from(table, hash_val, key, 0);
|
||
else {
|
||
new_tab = st_init_table_with_size(tab->type,
|
||
2 * tab->num_entries - 1);
|
||
new_entries = new_tab->entries;
|
||
}
|
||
ni = 0;
|
||
bins = new_tab->bins;
|
||
size_ind = get_size_ind(new_tab);
|
||
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))
|
||
continue;
|
||
if (&new_entries[ni] != curr_entry_ptr)
|
||
new_entries[ni] = *curr_entry_ptr;
|
||
if (EXPECT(bins != NULL, 1)) {
|
||
bin_ind = find_table_bin_ind_direct(new_tab, curr_entry_ptr->hash,
|
||
curr_entry_ptr->key);
|
||
st_assert(bin_ind != UNDEFINED_BIN_IND
|
||
&& (tab == new_tab || new_tab->rebuilds_num == 0)
|
||
&& IND_EMPTY_BIN_P(new_tab, bin_ind));
|
||
set_bin(bins, size_ind, bin_ind, ni + ENTRY_BASE);
|
||
}
|
||
new_tab->num_entries++;
|
||
ni++;
|
||
}
|
||
if (new_tab != tab) {
|
||
tab->entry_power = new_tab->entry_power;
|
||
tab->bin_power = new_tab->bin_power;
|
||
tab->size_ind = new_tab->size_ind;
|
||
st_assert (tab->num_entries == ni && new_tab->num_entries == ni);
|
||
if (tab->bins != NULL)
|
||
free(tab->bins);
|
||
tab->bins = new_tab->bins;
|
||
free(tab->entries);
|
||
tab->entries = new_tab->entries;
|
||
free(new_tab);
|
||
}
|
||
tab->entries_start = 0;
|
||
tab->entries_bound = tab->num_entries;
|
||
tab->rebuilds_num++;
|
||
#ifdef ST_DEBUG
|
||
st_check(tab);
|
||
#endif
|
||
}
|