Feature #13174 ยป improve_id_table.patch
| id_table.c | ||
|---|---|---|
| 
     #define ID_TABLE_DEBUG 0 
   | 
||
| 
     #endif 
   | 
||
| 
     #if ID_TABLE_DEBUG == 0 
   | 
||
| 
     #define NDEBUG 
   | 
||
| 
     #endif 
   | 
||
| 
     #include "ruby_assert.h" 
   | 
||
| 
     typedef rb_id_serial_t id_key_t; 
   | 
||
| 
     static inline ID 
   | 
||
| ... | ... | |
| 
        uses mark-bit on collisions - need extra 1 bit, 
   | 
||
| 
        ID is strictly 3 bits larger than rb_id_serial_t */ 
   | 
||
| 
     typedef struct rb_id_item { 
   | 
||
| 
         id_key_t key; 
   | 
||
| 
     #if SIZEOF_VALUE == 8 
   | 
||
| 
         int      collision; 
   | 
||
| 
     #endif 
   | 
||
| 
         VALUE    val; 
   | 
||
| 
     } item_t; 
   | 
||
| 
     struct rb_id_table { 
   | 
||
| 
         int capa; 
   | 
||
| 
         int num; 
   | 
||
| 
         int used; 
   | 
||
| 
         item_t *items; 
   | 
||
| 
         id_key_t *keys; 
   | 
||
| 
     }; 
   | 
||
| 
     #if SIZEOF_VALUE == 8 
   | 
||
| 
     #define ITEM_GET_KEY(tbl, i) ((tbl)->items[i].key) 
   | 
||
| 
     #define ITEM_KEY_ISSET(tbl, i) ((tbl)->items[i].key) 
   | 
||
| 
     #define ITEM_COLLIDED(tbl, i) ((tbl)->items[i].collision) 
   | 
||
| 
     #define ITEM_SET_COLLIDED(tbl, i) ((tbl)->items[i].collision = 1) 
   | 
||
| 
     #define ITEM_GET_KEY(tbl, i) ((tbl)->keys[i] >> 1) 
   | 
||
| 
     #define ITEM_KEY_ISSET(tbl, i) ((tbl)->keys[i] > 1) 
   | 
||
| 
     #define ITEM_COLLIDED(tbl, i) ((tbl)->keys[i] & 1) 
   | 
||
| 
     #define ITEM_SET_COLLIDED(tbl, i) ((tbl)->keys[i] |= 1) 
   | 
||
| 
     static inline void 
   | 
||
| 
     ITEM_SET_KEY(struct rb_id_table *tbl, int i, id_key_t key) 
   | 
||
| 
     { 
   | 
||
| 
         tbl->items[i].key = key; 
   | 
||
| 
         tbl->keys[i] = (key << 1) | ITEM_COLLIDED(tbl, i); 
   | 
||
| 
     } 
   | 
||
| 
     #else 
   | 
||
| 
     #define ITEM_GET_KEY(tbl, i) ((tbl)->items[i].key >> 1) 
   | 
||
| 
     #define ITEM_KEY_ISSET(tbl, i) ((tbl)->items[i].key > 1) 
   | 
||
| 
     #define ITEM_COLLIDED(tbl, i) ((tbl)->items[i].key & 1) 
   | 
||
| 
     #define ITEM_SET_COLLIDED(tbl, i) ((tbl)->items[i].key |= 1) 
   | 
||
| 
     #define ID_TABLE_VALUES(tbl) ((VALUE*)((tbl)->keys+(tbl)->capa)) 
   | 
||
| 
     #define ITEM_GET_VALUE(tbl, i) (ID_TABLE_VALUES(tbl)[i]) 
   | 
||
| 
     static inline void 
   | 
||
| 
     ITEM_SET_KEY(struct rb_id_table *tbl, int i, id_key_t key) 
   | 
||
| 
     ITEM_SET_VALUE(struct rb_id_table *tbl, int i, VALUE val) 
   | 
||
| 
     { 
   | 
||
| 
         tbl->items[i].key = (key << 1) | ITEM_COLLIDED(tbl, i); 
   | 
||
| 
         ID_TABLE_VALUES(tbl)[i] = val; 
   | 
||
| 
     } 
   | 
||
| 
     #endif 
   | 
||
| 
     #define ITEM_SIZE (sizeof(id_key_t) + sizeof(VALUE)) 
   | 
||
| 
     static inline int 
   | 
||
| 
     round_capa(int capa) 
   | 
||
| ... | ... | |
| 
         if (capa > 0) { 
   | 
||
| 
     	capa = round_capa(capa); 
   | 
||
| 
     	tbl->capa = (int)capa; 
   | 
||
| 
     	tbl->items = ZALLOC_N(item_t, capa); 
   | 
||
| 
     	tbl->keys = (id_key_t*)ruby_xcalloc(capa, ITEM_SIZE); 
   | 
||
| 
         } 
   | 
||
| 
         return tbl; 
   | 
||
| 
     } 
   | 
||
| ... | ... | |
| 
     void 
   | 
||
| 
     rb_id_table_free(struct rb_id_table *tbl) 
   | 
||
| 
     { 
   | 
||
| 
         xfree(tbl->items); 
   | 
||
| 
         xfree(tbl->keys); 
   | 
||
| 
         xfree(tbl); 
   | 
||
| 
     } 
   | 
||
| ... | ... | |
| 
     { 
   | 
||
| 
         tbl->num = 0; 
   | 
||
| 
         tbl->used = 0; 
   | 
||
| 
         MEMZERO(tbl->items, item_t, tbl->capa); 
   | 
||
| 
         memset(tbl->keys, 0, tbl->capa * ITEM_SIZE); 
   | 
||
| 
     } 
   | 
||
| 
     size_t 
   | 
||
| ... | ... | |
| 
     size_t 
   | 
||
| 
     rb_id_table_memsize(const struct rb_id_table *tbl) 
   | 
||
| 
     { 
   | 
||
| 
         return sizeof(item_t) * tbl->capa + sizeof(struct rb_id_table); 
   | 
||
| 
         return ITEM_SIZE * (size_t)tbl->capa + sizeof(struct rb_id_table); 
   | 
||
| 
     } 
   | 
||
| 
     static int 
   | 
||
| ... | ... | |
| 
         int mask = tbl->capa - 1; 
   | 
||
| 
         int ix = key & mask; 
   | 
||
| 
         int d = 1; 
   | 
||
| 
         assert(key != 0); 
   | 
||
| 
     #if ID_TABLE_DEBUG 
   | 
||
| 
         assert(key > 0); 
   | 
||
| 
     #endif 
   | 
||
| 
         while (ITEM_KEY_ISSET(tbl, ix)) { 
   | 
||
| 
     	ITEM_SET_COLLIDED(tbl, ix); 
   | 
||
| 
     	ix = (ix + d) & mask; 
   | 
||
| ... | ... | |
| 
     	tbl->used++; 
   | 
||
| 
         } 
   | 
||
| 
         ITEM_SET_KEY(tbl, ix, key); 
   | 
||
| 
         tbl->items[ix].val = val; 
   | 
||
| 
         ITEM_SET_VALUE(tbl, ix, val); 
   | 
||
| 
     } 
   | 
||
| 
     static int 
   | 
||
| ... | ... | |
| 
     	} 
   | 
||
| 
     	tbl->num--; 
   | 
||
| 
     	ITEM_SET_KEY(tbl, ix, 0); 
   | 
||
| 
     	tbl->items[ix].val = 0; 
   | 
||
| 
     	ITEM_SET_VALUE(tbl, ix, 0); 
   | 
||
| 
     	return TRUE; 
   | 
||
| 
         } else { 
   | 
||
| 
     	return FALSE; 
   | 
||
| ... | ... | |
| 
     static void 
   | 
||
| 
     hash_table_extend(struct rb_id_table* tbl) 
   | 
||
| 
     { 
   | 
||
| 
         /* fill rate 66% */ 
   | 
||
| 
         if (tbl->used + (tbl->used >> 1) >= tbl->capa) { 
   | 
||
| 
     	int new_cap = round_capa(tbl->num + (tbl->num >> 1)); 
   | 
||
| 
     	struct rb_id_table tmp_tbl; 
   | 
||
| 
     	int i; 
   | 
||
| 
     	item_t* old; 
   | 
||
| 
     	struct rb_id_table tmp_tbl = {0, 0, 0}; 
   | 
||
| 
     	if (new_cap < tbl->capa) { 
   | 
||
| 
     	    new_cap = round_capa(tbl->used + (tbl->used >> 1)); 
   | 
||
| 
     	} 
   | 
||
| 
     	tmp_tbl.capa = new_cap; 
   | 
||
| 
     	tmp_tbl.items = ZALLOC_N(item_t, new_cap); 
   | 
||
| 
     	id_key_t* old; 
   | 
||
| 
     	VALUE* values = ID_TABLE_VALUES(tbl); 
   | 
||
| 
     	rb_id_table_init(&tmp_tbl, tbl->num + (tbl->num >> 1) + 1); 
   | 
||
| 
     	for (i = 0; i < tbl->capa; i++) { 
   | 
||
| 
     	    id_key_t key = ITEM_GET_KEY(tbl, i); 
   | 
||
| 
     	    if (key != 0) { 
   | 
||
| 
     		hash_table_raw_insert(&tmp_tbl, key, tbl->items[i].val); 
   | 
||
| 
     		hash_table_raw_insert(&tmp_tbl, key, values[i]); 
   | 
||
| 
     	    } 
   | 
||
| 
     	} 
   | 
||
| 
     	old = tbl->items; 
   | 
||
| 
     	old = tbl->keys; 
   | 
||
| 
     	*tbl = tmp_tbl; 
   | 
||
| 
     	xfree(old); 
   | 
||
| 
         } 
   | 
||
| ... | ... | |
| 
         int index = hash_table_index(tbl, key); 
   | 
||
| 
         if (index >= 0) { 
   | 
||
| 
     	*valp = tbl->items[index].val; 
   | 
||
| 
     	*valp = ITEM_GET_VALUE(tbl, index); 
   | 
||
| 
     	return TRUE; 
   | 
||
| 
         } 
   | 
||
| 
         else { 
   | 
||
| ... | ... | |
| 
         const int index = hash_table_index(tbl, key); 
   | 
||
| 
         if (index >= 0) { 
   | 
||
| 
     	tbl->items[index].val = val; 
   | 
||
| 
     	ITEM_SET_VALUE(tbl, index, val); 
   | 
||
| 
         } 
   | 
||
| 
         else { 
   | 
||
| 
     	hash_table_extend(tbl); 
   | 
||
| ... | ... | |
| 
         for (i=0; i<capa; i++) { 
   | 
||
| 
     	if (ITEM_KEY_ISSET(tbl, i)) { 
   | 
||
| 
     	    const id_key_t key = ITEM_GET_KEY(tbl, i); 
   | 
||
| 
     	    enum rb_id_table_iterator_result ret = (*func)(key2id(key), tbl->items[i].val, data); 
   | 
||
| 
     	    assert(key != 0); 
   | 
||
| 
     	    const VALUE val = ITEM_GET_VALUE(tbl, i); 
   | 
||
| 
     	    enum rb_id_table_iterator_result ret = (*func)(key2id(key), val, data); 
   | 
||
| 
     	    if (ret == ID_TABLE_DELETE) 
   | 
||
| 
     		hash_delete_index(tbl, i); 
   | 
||
| ... | ... | |
| 
         for (i=0; i<capa; i++) { 
   | 
||
| 
     	if (ITEM_KEY_ISSET(tbl, i)) { 
   | 
||
| 
     	    enum rb_id_table_iterator_result ret = (*func)(tbl->items[i].val, data); 
   | 
||
| 
     	    const VALUE val = ITEM_GET_VALUE(tbl, i); 
   | 
||
| 
     	    enum rb_id_table_iterator_result ret = (*func)(val, data); 
   | 
||
| 
     	    if (ret == ID_TABLE_DELETE) 
   | 
||
| 
     		hash_delete_index(tbl, i); 
   | 
||
| symbol.c | ||
|---|---|---|
| 
     #include "symbol.h" 
   | 
||
| 
     #include "gc.h" 
   | 
||
| 
     #include "probes.h" 
   | 
||
| 
     #include "ruby_assert.h" 
   | 
||
| 
     #ifndef SYMBOL_DEBUG 
   | 
||
| 
     # define SYMBOL_DEBUG 0 
   | 
||
| ... | ... | |
| 
     { 
   | 
||
| 
         rb_id_serial_t next_serial = global_symbols.last_id + 1; 
   | 
||
| 
         if (next_serial == 0) { 
   | 
||
| 
         if (sizeof(next_serial) >= sizeof(ID) && 
   | 
||
| 
     	    next_serial >= (rb_id_serial_t)(~(ID)0 >> (ID_SCOPE_SHIFT + RUBY_SPECIAL_SHIFT))) { 
   | 
||
| 
     	return (ID)-1; 
   | 
||
| 
         } 
   | 
||
| 
         /* need at lest 1 bit for collision mark in id_table */ 
   | 
||
| 
         else if (sizeof(next_serial) < sizeof(ID) && 
   | 
||
| 
     	    next_serial >= ~(rb_id_serial_t)0 >> 1) { 
   | 
||
| 
     	return (ID)-1; 
   | 
||
| 
         } 
   | 
||
| 
         else { 
   | 
||
| ... | ... | |
| 
     	    VALUE fstr = RSYMBOL(sym)->fstr; 
   | 
||
| 
     	    ID num = next_id_base(); 
   | 
||
| 
     	    if (num == (ID)-1) { 
   | 
||
| 
     		fstr = rb_str_ellipsize(fstr, 20); 
   | 
||
| 
     		rb_raise(rb_eRuntimeError, 
   | 
||
| 
     				"symbol table overflow (symbol %"PRIsVALUE")", 
   | 
||
| 
     				fstr); 
   | 
||
| 
     	    } 
   | 
||
| 
     	    RSYMBOL(sym)->id = id |= num; 
   | 
||
| 
     	    /* make it permanent object */ 
   | 
||
| 
     	    set_id_entry(rb_id_to_serial(num), fstr, sym); 
   | 
||
| ... | ... | |
| 
     ID 
   | 
||
| 
     rb_make_internal_id(void) 
   | 
||
| 
     { 
   | 
||
| 
         return next_id_base() | ID_INTERNAL | ID_STATIC_SYM; 
   | 
||
| 
         ID nid = next_id_base(); 
   | 
||
| 
         /* make_internal_id called very rarely during initialization, 
   | 
||
| 
          * so don't bother with exception */ 
   | 
||
| 
         assert(nid != (ID)-1); 
   | 
||
| 
         return nid | ID_INTERNAL | ID_STATIC_SYM; 
   | 
||
| 
     } 
   | 
||
| 
     static int 
   | 
||
| 
     -  
   | 
||
| id_table.c | ||
|---|---|---|
| 
         if (tbl->capa > 0) { 
   | 
||
| 
     	int mask = tbl->capa - 1; 
   | 
||
| 
     	int ix = key & mask; 
   | 
||
| 
     	id_key_t mix = tbl->capa > 64 ? key : 0; 
   | 
||
| 
     	int d = 1; 
   | 
||
| 
     	while (key != ITEM_GET_KEY(tbl, ix)) { 
   | 
||
| 
     	    if (!ITEM_COLLIDED(tbl, ix)) 
   | 
||
| 
     		return -1; 
   | 
||
| 
     	    ix = (ix + d) & mask; 
   | 
||
| 
     	    d++; 
   | 
||
| 
     	    d += 1 + (mix >>= 7); 
   | 
||
| 
     	} 
   | 
||
| 
     	return ix; 
   | 
||
| 
         } 
   | 
||
| ... | ... | |
| 
     { 
   | 
||
| 
         int mask = tbl->capa - 1; 
   | 
||
| 
         int ix = key & mask; 
   | 
||
| 
         id_key_t mix = tbl->capa > 64 ? key : 0; 
   | 
||
| 
         int d = 1; 
   | 
||
| 
     #if ID_TABLE_DEBUG 
   | 
||
| 
         assert(key > 0); 
   | 
||
| ... | ... | |
| 
         while (ITEM_KEY_ISSET(tbl, ix)) { 
   | 
||
| 
     	ITEM_SET_COLLIDED(tbl, ix); 
   | 
||
| 
     	ix = (ix + d) & mask; 
   | 
||
| 
     	d++; 
   | 
||
| 
     	d += 1 + (mix >>= 7); 
   | 
||
| 
         } 
   | 
||
| 
         tbl->num++; 
   | 
||
| 
         if (!ITEM_COLLIDED(tbl, ix)) { 
   | 
||