Ooops, revert changeset ea9efa06c137

Change pushed by mistake, the patch is still under review :-/

"""
_tracemalloc: add domain to trace keys

* hashtable.h: key has now a variable size
* _tracemalloc uses (pointer: void*, domain: unsigned int) as key for traces
"""
This commit is contained in:
Victor Stinner 2016-03-21 14:36:39 +01:00
parent fac395681f
commit 322bc12c31
4 changed files with 121 additions and 270 deletions

View File

@ -3,7 +3,6 @@
#include "frameobject.h" #include "frameobject.h"
#include "pythread.h" #include "pythread.h"
#include "osdefs.h" #include "osdefs.h"
#include <stddef.h> /* For offsetof */
/* Trace memory blocks allocated by PyMem_RawMalloc() */ /* Trace memory blocks allocated by PyMem_RawMalloc() */
#define TRACE_RAW_MALLOC #define TRACE_RAW_MALLOC
@ -55,26 +54,6 @@ static PyThread_type_lock tables_lock;
# define TABLES_UNLOCK() # define TABLES_UNLOCK()
#endif #endif
typedef unsigned int domain_t;
/* FIXME: pack also? */
typedef struct {
void *ptr;
domain_t domain;
} pointer_t;
/* Size of pointer_t content, it can be smaller than sizeof(pointer_t) */
#define POINTER_T_SIZE \
(offsetof(pointer_t, domain) + sizeof(domain_t))
#define POINTER_T_FILL_PADDING(key) \
do { \
if (POINTER_T_SIZE != sizeof(pointer_t)) { \
memset((char *)&(key) + POINTER_T_SIZE, 0, \
sizeof(pointer_t) - POINTER_T_SIZE); \
} \
} while (0)
/* Pack the frame_t structure to reduce the memory footprint on 64-bit /* Pack the frame_t structure to reduce the memory footprint on 64-bit
architectures: 12 bytes instead of 16. This optimization might produce architectures: 12 bytes instead of 16. This optimization might produce
SIGBUS on architectures not supporting unaligned memory accesses (64-bit SIGBUS on architectures not supporting unaligned memory accesses (64-bit
@ -217,56 +196,23 @@ set_reentrant(int reentrant)
} }
#endif #endif
static Py_uhash_t
hashtable_hash_pointer(size_t key_size, const void *pkey)
{
pointer_t ptr;
Py_uhash_t hash;
assert(sizeof(ptr) == key_size);
ptr = *(pointer_t *)pkey;
hash = (Py_uhash_t)_Py_HashPointer(ptr.ptr);
hash ^= ptr.domain;
return hash;
}
static Py_uhash_t
hashtable_hash_pyobject(size_t key_size, const void *pkey)
{
PyObject *obj;
assert(key_size == sizeof(PyObject *));
obj = *(PyObject **)pkey;
return PyObject_Hash(obj);
}
static int static int
hashtable_compare_unicode(size_t key_size, const void *pkey, hashtable_compare_unicode(const void *key, const _Py_hashtable_entry_t *entry)
const _Py_hashtable_entry_t *entry)
{ {
PyObject *key, *entry_key; if (key != NULL && entry->key != NULL)
return (PyUnicode_Compare((PyObject *)key, (PyObject *)entry->key) == 0);
assert(sizeof(key) == key_size);
key = *(PyObject **)pkey;
assert(sizeof(entry_key) == key_size);
entry_key = *(PyObject **)_Py_HASHTABLE_ENTRY_KEY(entry);
if (key != NULL && entry_key != NULL)
return (PyUnicode_Compare(key, entry_key) == 0);
else else
return key == entry_key; return key == entry->key;
} }
static _Py_hashtable_allocator_t hashtable_alloc = {malloc, free}; static _Py_hashtable_allocator_t hashtable_alloc = {malloc, free};
static _Py_hashtable_t * static _Py_hashtable_t *
hashtable_new(size_t key_size, size_t data_size, hashtable_new(size_t data_size,
_Py_hashtable_hash_func hash_func, _Py_hashtable_hash_func hash_func,
_Py_hashtable_compare_func compare_func) _Py_hashtable_compare_func compare_func)
{ {
return _Py_hashtable_new_full(key_size, data_size, 0, return _Py_hashtable_new_full(data_size, 0,
hash_func, compare_func, hash_func, compare_func,
NULL, NULL, NULL, &hashtable_alloc); NULL, NULL, NULL, &hashtable_alloc);
} }
@ -284,26 +230,20 @@ raw_free(void *ptr)
} }
static Py_uhash_t static Py_uhash_t
hashtable_hash_traceback(size_t key_size, const void *pkey) hashtable_hash_traceback(const void *key)
{ {
const traceback_t *traceback = *(const traceback_t **)pkey; const traceback_t *traceback = key;
assert(key_size == sizeof(const traceback_t *));
return traceback->hash; return traceback->hash;
} }
static int static int
hashtable_compare_traceback(size_t key_size, const void *pkey, hashtable_compare_traceback(const traceback_t *traceback1,
const _Py_hashtable_entry_t *he) const _Py_hashtable_entry_t *he)
{ {
traceback_t *traceback1, *traceback2; const traceback_t *traceback2 = he->key;
const frame_t *frame1, *frame2; const frame_t *frame1, *frame2;
int i; int i;
assert(sizeof(traceback1) == key_size);
assert(sizeof(traceback2) == key_size);
traceback1 = *(traceback_t **)pkey;
traceback2 = *(traceback_t **)_Py_HASHTABLE_ENTRY_KEY(he);
if (traceback1->nframe != traceback2->nframe) if (traceback1->nframe != traceback2->nframe)
return 0; return 0;
@ -372,16 +312,15 @@ tracemalloc_get_frame(PyFrameObject *pyframe, frame_t *frame)
} }
/* intern the filename */ /* intern the filename */
entry = _Py_HASHTABLE_GET_ENTRY(tracemalloc_filenames, filename); entry = _Py_hashtable_get_entry(tracemalloc_filenames, filename);
if (entry != NULL) { if (entry != NULL) {
assert(sizeof(filename) == tracemalloc_filenames->key_size); filename = (PyObject *)entry->key;
filename = *(PyObject **)_Py_HASHTABLE_ENTRY_KEY(entry);
} }
else { else {
/* tracemalloc_filenames is responsible to keep a reference /* tracemalloc_filenames is responsible to keep a reference
to the filename */ to the filename */
Py_INCREF(filename); Py_INCREF(filename);
if (_Py_HASHTABLE_SET_NODATA(tracemalloc_filenames, filename) < 0) { if (_Py_hashtable_set(tracemalloc_filenames, filename, NULL, 0) < 0) {
Py_DECREF(filename); Py_DECREF(filename);
#ifdef TRACE_DEBUG #ifdef TRACE_DEBUG
tracemalloc_error("failed to intern the filename"); tracemalloc_error("failed to intern the filename");
@ -464,10 +403,9 @@ traceback_new(void)
traceback->hash = traceback_hash(traceback); traceback->hash = traceback_hash(traceback);
/* intern the traceback */ /* intern the traceback */
entry = _Py_HASHTABLE_GET_ENTRY(tracemalloc_tracebacks, traceback); entry = _Py_hashtable_get_entry(tracemalloc_tracebacks, traceback);
if (entry != NULL) { if (entry != NULL) {
assert(sizeof(traceback) == tracemalloc_tracebacks->key_size); traceback = (traceback_t *)entry->key;
traceback = *(traceback_t **)_Py_HASHTABLE_ENTRY_KEY(entry);
} }
else { else {
traceback_t *copy; traceback_t *copy;
@ -484,7 +422,7 @@ traceback_new(void)
} }
memcpy(copy, traceback, traceback_size); memcpy(copy, traceback, traceback_size);
if (_Py_HASHTABLE_SET_NODATA(tracemalloc_tracebacks, copy) < 0) { if (_Py_hashtable_set(tracemalloc_tracebacks, copy, NULL, 0) < 0) {
raw_free(copy); raw_free(copy);
#ifdef TRACE_DEBUG #ifdef TRACE_DEBUG
tracemalloc_error("failed to intern the traceback: putdata failed"); tracemalloc_error("failed to intern the traceback: putdata failed");
@ -497,9 +435,8 @@ traceback_new(void)
} }
static int static int
tracemalloc_add_trace(void *ptr, domain_t domain, size_t size) tracemalloc_add_trace(void *ptr, size_t size)
{ {
pointer_t key;
traceback_t *traceback; traceback_t *traceback;
trace_t trace; trace_t trace;
int res; int res;
@ -508,14 +445,10 @@ tracemalloc_add_trace(void *ptr, domain_t domain, size_t size)
if (traceback == NULL) if (traceback == NULL)
return -1; return -1;
key.ptr = ptr;
key.domain = 0;
POINTER_T_FILL_PADDING(key);
trace.size = size; trace.size = size;
trace.traceback = traceback; trace.traceback = traceback;
res = _Py_HASHTABLE_SET(tracemalloc_traces, key, trace); res = _Py_HASHTABLE_SET(tracemalloc_traces, ptr, trace);
if (res == 0) { if (res == 0) {
assert(tracemalloc_traced_memory <= PY_SIZE_MAX - size); assert(tracemalloc_traced_memory <= PY_SIZE_MAX - size);
tracemalloc_traced_memory += size; tracemalloc_traced_memory += size;
@ -527,16 +460,11 @@ tracemalloc_add_trace(void *ptr, domain_t domain, size_t size)
} }
static void static void
tracemalloc_remove_trace(void *ptr, domain_t domain) tracemalloc_remove_trace(void *ptr)
{ {
pointer_t key;
trace_t trace; trace_t trace;
key.ptr = ptr; if (_Py_hashtable_pop(tracemalloc_traces, ptr, &trace, sizeof(trace))) {
key.domain = domain;
POINTER_T_FILL_PADDING(key);
if (_Py_HASHTABLE_POP(tracemalloc_traces, key, trace)) {
assert(tracemalloc_traced_memory >= trace.size); assert(tracemalloc_traced_memory >= trace.size);
tracemalloc_traced_memory -= trace.size; tracemalloc_traced_memory -= trace.size;
} }
@ -558,7 +486,7 @@ tracemalloc_alloc(int use_calloc, void *ctx, size_t nelem, size_t elsize)
return NULL; return NULL;
TABLES_LOCK(); TABLES_LOCK();
if (tracemalloc_add_trace(ptr, 0, nelem * elsize) < 0) { if (tracemalloc_add_trace(ptr, nelem * elsize) < 0) {
/* Failed to allocate a trace for the new memory block */ /* Failed to allocate a trace for the new memory block */
TABLES_UNLOCK(); TABLES_UNLOCK();
alloc->free(alloc->ctx, ptr); alloc->free(alloc->ctx, ptr);
@ -582,9 +510,9 @@ tracemalloc_realloc(void *ctx, void *ptr, size_t new_size)
/* an existing memory block has been resized */ /* an existing memory block has been resized */
TABLES_LOCK(); TABLES_LOCK();
tracemalloc_remove_trace(ptr, 0); tracemalloc_remove_trace(ptr);
if (tracemalloc_add_trace(ptr2, 0, new_size) < 0) { if (tracemalloc_add_trace(ptr2, new_size) < 0) {
/* Memory allocation failed. The error cannot be reported to /* Memory allocation failed. The error cannot be reported to
the caller, because realloc() may already have shrinked the the caller, because realloc() may already have shrinked the
memory block and so removed bytes. memory block and so removed bytes.
@ -602,7 +530,7 @@ tracemalloc_realloc(void *ctx, void *ptr, size_t new_size)
/* new allocation */ /* new allocation */
TABLES_LOCK(); TABLES_LOCK();
if (tracemalloc_add_trace(ptr2, 0, new_size) < 0) { if (tracemalloc_add_trace(ptr2, new_size) < 0) {
/* Failed to allocate a trace for the new memory block */ /* Failed to allocate a trace for the new memory block */
TABLES_UNLOCK(); TABLES_UNLOCK();
alloc->free(alloc->ctx, ptr2); alloc->free(alloc->ctx, ptr2);
@ -627,7 +555,7 @@ tracemalloc_free(void *ctx, void *ptr)
alloc->free(alloc->ctx, ptr); alloc->free(alloc->ctx, ptr);
TABLES_LOCK(); TABLES_LOCK();
tracemalloc_remove_trace(ptr, 0); tracemalloc_remove_trace(ptr);
TABLES_UNLOCK(); TABLES_UNLOCK();
} }
@ -682,7 +610,7 @@ tracemalloc_realloc_gil(void *ctx, void *ptr, size_t new_size)
ptr2 = alloc->realloc(alloc->ctx, ptr, new_size); ptr2 = alloc->realloc(alloc->ctx, ptr, new_size);
if (ptr2 != NULL && ptr != NULL) { if (ptr2 != NULL && ptr != NULL) {
TABLES_LOCK(); TABLES_LOCK();
tracemalloc_remove_trace(ptr, 0); tracemalloc_remove_trace(ptr);
TABLES_UNLOCK(); TABLES_UNLOCK();
} }
return ptr2; return ptr2;
@ -761,7 +689,7 @@ tracemalloc_raw_realloc(void *ctx, void *ptr, size_t new_size)
if (ptr2 != NULL && ptr != NULL) { if (ptr2 != NULL && ptr != NULL) {
TABLES_LOCK(); TABLES_LOCK();
tracemalloc_remove_trace(ptr, 0); tracemalloc_remove_trace(ptr);
TABLES_UNLOCK(); TABLES_UNLOCK();
} }
return ptr2; return ptr2;
@ -786,27 +714,17 @@ tracemalloc_raw_realloc(void *ctx, void *ptr, size_t new_size)
#endif /* TRACE_RAW_MALLOC */ #endif /* TRACE_RAW_MALLOC */
static int static int
tracemalloc_clear_filename(_Py_hashtable_t *ht, _Py_hashtable_entry_t *entry, tracemalloc_clear_filename(_Py_hashtable_entry_t *entry, void *user_data)
void *user_data)
{ {
PyObject *filename; PyObject *filename = (PyObject *)entry->key;
assert(sizeof(filename) == ht->key_size);
filename = *(PyObject **)_Py_HASHTABLE_ENTRY_KEY(entry);
Py_DECREF(filename); Py_DECREF(filename);
return 0; return 0;
} }
static int static int
traceback_free_traceback(_Py_hashtable_t *ht, _Py_hashtable_entry_t *entry, traceback_free_traceback(_Py_hashtable_entry_t *entry, void *user_data)
void *user_data)
{ {
traceback_t *traceback; traceback_t *traceback = (traceback_t *)entry->key;
assert(sizeof(traceback) == ht->key_size);
traceback = *(traceback_t **)_Py_HASHTABLE_ENTRY_KEY(entry);
raw_free(traceback); raw_free(traceback);
return 0; return 0;
} }
@ -873,16 +791,16 @@ tracemalloc_init(void)
} }
#endif #endif
tracemalloc_filenames = hashtable_new(sizeof(PyObject *), 0, tracemalloc_filenames = hashtable_new(0,
hashtable_hash_pyobject, (_Py_hashtable_hash_func)PyObject_Hash,
hashtable_compare_unicode); hashtable_compare_unicode);
tracemalloc_tracebacks = hashtable_new(sizeof(traceback_t *), 0, tracemalloc_tracebacks = hashtable_new(0,
hashtable_hash_traceback, (_Py_hashtable_hash_func)hashtable_hash_traceback,
hashtable_compare_traceback); (_Py_hashtable_compare_func)hashtable_compare_traceback);
tracemalloc_traces = hashtable_new(sizeof(pointer_t), sizeof(trace_t), tracemalloc_traces = hashtable_new(sizeof(trace_t),
hashtable_hash_pointer, _Py_hashtable_hash_ptr,
_Py_hashtable_compare_direct); _Py_hashtable_compare_direct);
if (tracemalloc_filenames == NULL || tracemalloc_tracebacks == NULL if (tracemalloc_filenames == NULL || tracemalloc_tracebacks == NULL
@ -1147,15 +1065,14 @@ typedef struct {
} get_traces_t; } get_traces_t;
static int static int
tracemalloc_get_traces_fill(_Py_hashtable_t *traces, _Py_hashtable_entry_t *entry, tracemalloc_get_traces_fill(_Py_hashtable_entry_t *entry, void *user_data)
void *user_data)
{ {
get_traces_t *get_traces = user_data; get_traces_t *get_traces = user_data;
trace_t *trace; trace_t *trace;
PyObject *tracemalloc_obj; PyObject *tracemalloc_obj;
int res; int res;
trace = (trace_t *)_Py_HASHTABLE_ENTRY_DATA(traces, entry); trace = (trace_t *)_Py_HASHTABLE_ENTRY_DATA(entry);
tracemalloc_obj = trace_to_pyobject(trace, get_traces->tracebacks); tracemalloc_obj = trace_to_pyobject(trace, get_traces->tracebacks);
if (tracemalloc_obj == NULL) if (tracemalloc_obj == NULL)
@ -1170,11 +1087,9 @@ tracemalloc_get_traces_fill(_Py_hashtable_t *traces, _Py_hashtable_entry_t *entr
} }
static int static int
tracemalloc_pyobject_decref_cb(_Py_hashtable_t *tracebacks, tracemalloc_pyobject_decref_cb(_Py_hashtable_entry_t *entry, void *user_data)
_Py_hashtable_entry_t *entry,
void *user_data)
{ {
PyObject *obj = (PyObject *)_Py_HASHTABLE_ENTRY_DATA_AS_VOID_P(tracebacks, entry); PyObject *obj = (PyObject *)_Py_HASHTABLE_ENTRY_DATA_AS_VOID_P(entry);
Py_DECREF(obj); Py_DECREF(obj);
return 0; return 0;
} }
@ -1205,7 +1120,7 @@ py_tracemalloc_get_traces(PyObject *self, PyObject *obj)
/* the traceback hash table is used temporarily to intern traceback tuple /* the traceback hash table is used temporarily to intern traceback tuple
of (filename, lineno) tuples */ of (filename, lineno) tuples */
get_traces.tracebacks = hashtable_new(sizeof(traceback_t *), sizeof(PyObject *), get_traces.tracebacks = hashtable_new(sizeof(PyObject *),
_Py_hashtable_hash_ptr, _Py_hashtable_hash_ptr,
_Py_hashtable_compare_direct); _Py_hashtable_compare_direct);
if (get_traces.tracebacks == NULL) { if (get_traces.tracebacks == NULL) {
@ -1247,21 +1162,16 @@ finally:
} }
static traceback_t* static traceback_t*
tracemalloc_get_traceback(const void *ptr, domain_t domain) tracemalloc_get_traceback(const void *ptr)
{ {
pointer_t key;
trace_t trace; trace_t trace;
int found; int found;
if (!tracemalloc_config.tracing) if (!tracemalloc_config.tracing)
return NULL; return NULL;
key.ptr = (void *)ptr;
key.domain = domain;
POINTER_T_FILL_PADDING(key);
TABLES_LOCK(); TABLES_LOCK();
found = _Py_HASHTABLE_GET(tracemalloc_traces, key, trace); found = _Py_HASHTABLE_GET(tracemalloc_traces, ptr, trace);
TABLES_UNLOCK(); TABLES_UNLOCK();
if (!found) if (!found)
@ -1292,7 +1202,7 @@ py_tracemalloc_get_object_traceback(PyObject *self, PyObject *obj)
else else
ptr = (void *)obj; ptr = (void *)obj;
traceback = tracemalloc_get_traceback(ptr, 0); traceback = tracemalloc_get_traceback(ptr);
if (traceback == NULL) if (traceback == NULL)
Py_RETURN_NONE; Py_RETURN_NONE;
@ -1319,7 +1229,7 @@ _PyMem_DumpTraceback(int fd, const void *ptr)
traceback_t *traceback; traceback_t *traceback;
int i; int i;
traceback = tracemalloc_get_traceback(ptr, 0); traceback = tracemalloc_get_traceback(ptr);
if (traceback == NULL) if (traceback == NULL)
return; return;

View File

@ -59,7 +59,7 @@
#define ENTRY_NEXT(ENTRY) \ #define ENTRY_NEXT(ENTRY) \
((_Py_hashtable_entry_t *)_Py_SLIST_ITEM_NEXT(ENTRY)) ((_Py_hashtable_entry_t *)_Py_SLIST_ITEM_NEXT(ENTRY))
#define HASHTABLE_ITEM_SIZE(HT) \ #define HASHTABLE_ITEM_SIZE(HT) \
(sizeof(_Py_hashtable_entry_t) + (HT)->key_size + (HT)->data_size) (sizeof(_Py_hashtable_entry_t) + (HT)->data_size)
/* Forward declaration */ /* Forward declaration */
static void hashtable_rehash(_Py_hashtable_t *ht); static void hashtable_rehash(_Py_hashtable_t *ht);
@ -88,21 +88,21 @@ _Py_slist_remove(_Py_slist_t *list, _Py_slist_item_t *previous,
} }
Py_uhash_t Py_uhash_t
_Py_hashtable_hash_ptr(size_t key_size, const void *pkey) _Py_hashtable_hash_int(const void *key)
{ {
void *key; return (Py_uhash_t)key;
}
assert(key_size == sizeof(void *));
key = *(void**)pkey;
Py_uhash_t
_Py_hashtable_hash_ptr(const void *key)
{
return (Py_uhash_t)_Py_HashPointer((void *)key); return (Py_uhash_t)_Py_HashPointer((void *)key);
} }
int int
_Py_hashtable_compare_direct(size_t key_size, const void *pkey, _Py_hashtable_compare_direct(const void *key, const _Py_hashtable_entry_t *entry)
const _Py_hashtable_entry_t *entry)
{ {
return (memcmp(pkey, _Py_HASHTABLE_ENTRY_KEY(entry), key_size) == 0); return entry->key == key;
} }
/* makes sure the real size of the buckets array is a power of 2 */ /* makes sure the real size of the buckets array is a power of 2 */
@ -119,8 +119,7 @@ round_size(size_t s)
} }
_Py_hashtable_t * _Py_hashtable_t *
_Py_hashtable_new_full(size_t key_size, size_t data_size, _Py_hashtable_new_full(size_t data_size, size_t init_size,
size_t init_size,
_Py_hashtable_hash_func hash_func, _Py_hashtable_hash_func hash_func,
_Py_hashtable_compare_func compare_func, _Py_hashtable_compare_func compare_func,
_Py_hashtable_copy_data_func copy_data_func, _Py_hashtable_copy_data_func copy_data_func,
@ -145,7 +144,6 @@ _Py_hashtable_new_full(size_t key_size, size_t data_size,
ht->num_buckets = round_size(init_size); ht->num_buckets = round_size(init_size);
ht->entries = 0; ht->entries = 0;
ht->key_size = key_size;
ht->data_size = data_size; ht->data_size = data_size;
buckets_size = ht->num_buckets * sizeof(ht->buckets[0]); buckets_size = ht->num_buckets * sizeof(ht->buckets[0]);
@ -166,12 +164,11 @@ _Py_hashtable_new_full(size_t key_size, size_t data_size,
} }
_Py_hashtable_t * _Py_hashtable_t *
_Py_hashtable_new(size_t key_size, size_t data_size, _Py_hashtable_new(size_t data_size,
_Py_hashtable_hash_func hash_func, _Py_hashtable_hash_func hash_func,
_Py_hashtable_compare_func compare_func) _Py_hashtable_compare_func compare_func)
{ {
return _Py_hashtable_new_full(key_size, data_size, return _Py_hashtable_new_full(data_size, HASHTABLE_MIN_SIZE,
HASHTABLE_MIN_SIZE,
hash_func, compare_func, hash_func, compare_func,
NULL, NULL, NULL, NULL); NULL, NULL, NULL, NULL);
} }
@ -198,7 +195,7 @@ _Py_hashtable_size(_Py_hashtable_t *ht)
for (entry = TABLE_HEAD(ht, hv); entry; entry = ENTRY_NEXT(entry)) { for (entry = TABLE_HEAD(ht, hv); entry; entry = ENTRY_NEXT(entry)) {
void *data; void *data;
data = _Py_HASHTABLE_ENTRY_DATA_AS_VOID_P(ht, entry); data = _Py_HASHTABLE_ENTRY_DATA_AS_VOID_P(entry);
size += ht->get_data_size_func(data); size += ht->get_data_size_func(data);
} }
} }
@ -248,21 +245,17 @@ _Py_hashtable_print_stats(_Py_hashtable_t *ht)
/* Get an entry. Return NULL if the key does not exist. */ /* Get an entry. Return NULL if the key does not exist. */
_Py_hashtable_entry_t * _Py_hashtable_entry_t *
_Py_hashtable_get_entry(_Py_hashtable_t *ht, _Py_hashtable_get_entry(_Py_hashtable_t *ht, const void *key)
size_t key_size, const void *pkey)
{ {
Py_uhash_t key_hash; Py_uhash_t key_hash;
size_t index; size_t index;
_Py_hashtable_entry_t *entry; _Py_hashtable_entry_t *entry;
assert(key_size == ht->key_size); key_hash = ht->hash_func(key);
key_hash = ht->hash_func(key_size, pkey);
index = key_hash & (ht->num_buckets - 1); index = key_hash & (ht->num_buckets - 1);
for (entry = TABLE_HEAD(ht, index); entry != NULL; entry = ENTRY_NEXT(entry)) { for (entry = TABLE_HEAD(ht, index); entry != NULL; entry = ENTRY_NEXT(entry)) {
if (entry->key_hash == key_hash if (entry->key_hash == key_hash && ht->compare_func(key, entry))
&& ht->compare_func(key_size, pkey, entry))
break; break;
} }
@ -270,20 +263,18 @@ _Py_hashtable_get_entry(_Py_hashtable_t *ht,
} }
static int static int
_hashtable_pop_entry(_Py_hashtable_t *ht, size_t key_size, const void *pkey, _hashtable_pop_entry(_Py_hashtable_t *ht, const void *key, void *data, size_t data_size)
void *data, size_t data_size)
{ {
Py_uhash_t key_hash; Py_uhash_t key_hash;
size_t index; size_t index;
_Py_hashtable_entry_t *entry, *previous; _Py_hashtable_entry_t *entry, *previous;
key_hash = ht->hash_func(key_size, pkey); key_hash = ht->hash_func(key);
index = key_hash & (ht->num_buckets - 1); index = key_hash & (ht->num_buckets - 1);
previous = NULL; previous = NULL;
for (entry = TABLE_HEAD(ht, index); entry != NULL; entry = ENTRY_NEXT(entry)) { for (entry = TABLE_HEAD(ht, index); entry != NULL; entry = ENTRY_NEXT(entry)) {
if (entry->key_hash == key_hash if (entry->key_hash == key_hash && ht->compare_func(key, entry))
&& ht->compare_func(key_size, pkey, entry))
break; break;
previous = entry; previous = entry;
} }
@ -307,8 +298,8 @@ _hashtable_pop_entry(_Py_hashtable_t *ht, size_t key_size, const void *pkey,
/* Add a new entry to the hash. The key must not be present in the hash table. /* Add a new entry to the hash. The key must not be present in the hash table.
Return 0 on success, -1 on memory error. */ Return 0 on success, -1 on memory error. */
int int
_Py_hashtable_set(_Py_hashtable_t *ht, size_t key_size, const void *pkey, _Py_hashtable_set(_Py_hashtable_t *ht, const void *key,
size_t data_size, void *data) void *data, size_t data_size)
{ {
Py_uhash_t key_hash; Py_uhash_t key_hash;
size_t index; size_t index;
@ -319,11 +310,11 @@ _Py_hashtable_set(_Py_hashtable_t *ht, size_t key_size, const void *pkey,
/* Don't write the assertion on a single line because it is interesting /* Don't write the assertion on a single line because it is interesting
to know the duplicated entry if the assertion failed. The entry can to know the duplicated entry if the assertion failed. The entry can
be read using a debugger. */ be read using a debugger. */
entry = _Py_hashtable_get_entry(ht, key_size, pkey); entry = _Py_hashtable_get_entry(ht, key);
assert(entry == NULL); assert(entry == NULL);
#endif #endif
key_hash = ht->hash_func(key_size, pkey); key_hash = ht->hash_func(key);
index = key_hash & (ht->num_buckets - 1); index = key_hash & (ht->num_buckets - 1);
entry = ht->alloc.malloc(HASHTABLE_ITEM_SIZE(ht)); entry = ht->alloc.malloc(HASHTABLE_ITEM_SIZE(ht));
@ -332,11 +323,11 @@ _Py_hashtable_set(_Py_hashtable_t *ht, size_t key_size, const void *pkey,
return -1; return -1;
} }
entry->key = (void *)key;
entry->key_hash = key_hash; entry->key_hash = key_hash;
memcpy(_Py_HASHTABLE_ENTRY_KEY(entry), pkey, key_size);
assert(data_size == ht->data_size); assert(data_size == ht->data_size);
memcpy(_Py_HASHTABLE_ENTRY_DATA(ht, entry), data, data_size); memcpy(_Py_HASHTABLE_ENTRY_DATA(entry), data, data_size);
_Py_slist_prepend(&ht->buckets[index], (_Py_slist_item_t*)entry); _Py_slist_prepend(&ht->buckets[index], (_Py_slist_item_t*)entry);
ht->entries++; ht->entries++;
@ -349,14 +340,13 @@ _Py_hashtable_set(_Py_hashtable_t *ht, size_t key_size, const void *pkey,
/* Get data from an entry. Copy entry data into data and return 1 if the entry /* Get data from an entry. Copy entry data into data and return 1 if the entry
exists, return 0 if the entry does not exist. */ exists, return 0 if the entry does not exist. */
int int
_Py_hashtable_get(_Py_hashtable_t *ht, size_t key_size,const void *pkey, _Py_hashtable_get(_Py_hashtable_t *ht, const void *key, void *data, size_t data_size)
size_t data_size, void *data)
{ {
_Py_hashtable_entry_t *entry; _Py_hashtable_entry_t *entry;
assert(data != NULL); assert(data != NULL);
entry = _Py_hashtable_get_entry(ht, key_size, pkey); entry = _Py_hashtable_get_entry(ht, key);
if (entry == NULL) if (entry == NULL)
return 0; return 0;
_Py_HASHTABLE_ENTRY_READ_DATA(ht, data, data_size, entry); _Py_HASHTABLE_ENTRY_READ_DATA(ht, data, data_size, entry);
@ -364,23 +354,22 @@ _Py_hashtable_get(_Py_hashtable_t *ht, size_t key_size,const void *pkey,
} }
int int
_Py_hashtable_pop(_Py_hashtable_t *ht, size_t key_size, const void *pkey, _Py_hashtable_pop(_Py_hashtable_t *ht, const void *key, void *data, size_t data_size)
size_t data_size, void *data)
{ {
assert(data != NULL); assert(data != NULL);
assert(ht->free_data_func == NULL); assert(ht->free_data_func == NULL);
return _hashtable_pop_entry(ht, key_size, pkey, data, data_size); return _hashtable_pop_entry(ht, key, data, data_size);
} }
/* Delete an entry. The entry must exist. */ /* Delete an entry. The entry must exist. */
void void
_Py_hashtable_delete(_Py_hashtable_t *ht, size_t key_size, const void *pkey) _Py_hashtable_delete(_Py_hashtable_t *ht, const void *key)
{ {
#ifndef NDEBUG #ifndef NDEBUG
int found = _hashtable_pop_entry(ht, key_size, pkey, NULL, 0); int found = _hashtable_pop_entry(ht, key, NULL, 0);
assert(found); assert(found);
#else #else
(void)_hashtable_pop_entry(ht, key_size, pkey, NULL, 0); (void)_hashtable_pop_entry(ht, key, NULL, 0);
#endif #endif
} }
@ -389,7 +378,7 @@ _Py_hashtable_delete(_Py_hashtable_t *ht, size_t key_size, const void *pkey)
stops if a non-zero value is returned. */ stops if a non-zero value is returned. */
int int
_Py_hashtable_foreach(_Py_hashtable_t *ht, _Py_hashtable_foreach(_Py_hashtable_t *ht,
_Py_hashtable_foreach_func func, int (*func) (_Py_hashtable_entry_t *entry, void *arg),
void *arg) void *arg)
{ {
_Py_hashtable_entry_t *entry; _Py_hashtable_entry_t *entry;
@ -397,7 +386,7 @@ _Py_hashtable_foreach(_Py_hashtable_t *ht,
for (hv = 0; hv < ht->num_buckets; hv++) { for (hv = 0; hv < ht->num_buckets; hv++) {
for (entry = TABLE_HEAD(ht, hv); entry; entry = ENTRY_NEXT(entry)) { for (entry = TABLE_HEAD(ht, hv); entry; entry = ENTRY_NEXT(entry)) {
int res = func(ht, entry, arg); int res = func(entry, arg);
if (res) if (res)
return res; return res;
} }
@ -408,7 +397,6 @@ _Py_hashtable_foreach(_Py_hashtable_t *ht,
static void static void
hashtable_rehash(_Py_hashtable_t *ht) hashtable_rehash(_Py_hashtable_t *ht)
{ {
const size_t key_size = ht->key_size;
size_t buckets_size, new_size, bucket; size_t buckets_size, new_size, bucket;
_Py_slist_t *old_buckets = NULL; _Py_slist_t *old_buckets = NULL;
size_t old_num_buckets; size_t old_num_buckets;
@ -437,8 +425,7 @@ hashtable_rehash(_Py_hashtable_t *ht)
for (entry = BUCKETS_HEAD(old_buckets[bucket]); entry != NULL; entry = next) { for (entry = BUCKETS_HEAD(old_buckets[bucket]); entry != NULL; entry = next) {
size_t entry_index; size_t entry_index;
assert(ht->hash_func(entry->key) == entry->key_hash);
assert(ht->hash_func(key_size, _Py_HASHTABLE_ENTRY_KEY(entry)) == entry->key_hash);
next = ENTRY_NEXT(entry); next = ENTRY_NEXT(entry);
entry_index = entry->key_hash & (new_size - 1); entry_index = entry->key_hash & (new_size - 1);
@ -459,7 +446,7 @@ _Py_hashtable_clear(_Py_hashtable_t *ht)
for (entry = TABLE_HEAD(ht, i); entry != NULL; entry = next) { for (entry = TABLE_HEAD(ht, i); entry != NULL; entry = next) {
next = ENTRY_NEXT(entry); next = ENTRY_NEXT(entry);
if (ht->free_data_func) if (ht->free_data_func)
ht->free_data_func(_Py_HASHTABLE_ENTRY_DATA_AS_VOID_P(ht, entry)); ht->free_data_func(_Py_HASHTABLE_ENTRY_DATA_AS_VOID_P(entry));
ht->alloc.free(entry); ht->alloc.free(entry);
} }
_Py_slist_init(&ht->buckets[i]); _Py_slist_init(&ht->buckets[i]);
@ -478,7 +465,7 @@ _Py_hashtable_destroy(_Py_hashtable_t *ht)
while (entry) { while (entry) {
_Py_slist_item_t *entry_next = entry->next; _Py_slist_item_t *entry_next = entry->next;
if (ht->free_data_func) if (ht->free_data_func)
ht->free_data_func(_Py_HASHTABLE_ENTRY_DATA_AS_VOID_P(ht, entry)); ht->free_data_func(_Py_HASHTABLE_ENTRY_DATA_AS_VOID_P(entry));
ht->alloc.free(entry); ht->alloc.free(entry);
entry = entry_next; entry = entry_next;
} }
@ -492,16 +479,13 @@ _Py_hashtable_destroy(_Py_hashtable_t *ht)
_Py_hashtable_t * _Py_hashtable_t *
_Py_hashtable_copy(_Py_hashtable_t *src) _Py_hashtable_copy(_Py_hashtable_t *src)
{ {
const size_t key_size = src->key_size;
const size_t data_size = src->data_size;
_Py_hashtable_t *dst; _Py_hashtable_t *dst;
_Py_hashtable_entry_t *entry; _Py_hashtable_entry_t *entry;
size_t bucket; size_t bucket;
int err; int err;
void *data, *new_data; void *data, *new_data;
dst = _Py_hashtable_new_full(key_size, data_size, dst = _Py_hashtable_new_full(src->data_size, src->num_buckets,
src->num_buckets,
src->hash_func, src->compare_func, src->hash_func, src->compare_func,
src->copy_data_func, src->free_data_func, src->copy_data_func, src->free_data_func,
src->get_data_size_func, &src->alloc); src->get_data_size_func, &src->alloc);
@ -512,20 +496,17 @@ _Py_hashtable_copy(_Py_hashtable_t *src)
entry = TABLE_HEAD(src, bucket); entry = TABLE_HEAD(src, bucket);
for (; entry; entry = ENTRY_NEXT(entry)) { for (; entry; entry = ENTRY_NEXT(entry)) {
if (src->copy_data_func) { if (src->copy_data_func) {
data = _Py_HASHTABLE_ENTRY_DATA_AS_VOID_P(src, entry); data = _Py_HASHTABLE_ENTRY_DATA_AS_VOID_P(entry);
new_data = src->copy_data_func(data); new_data = src->copy_data_func(data);
if (new_data != NULL) if (new_data != NULL)
err = _Py_hashtable_set(dst, key_size, err = _Py_hashtable_set(dst, entry->key,
_Py_HASHTABLE_ENTRY_KEY(entry), &new_data, src->data_size);
data_size, &new_data);
else else
err = 1; err = 1;
} }
else { else {
data = _Py_HASHTABLE_ENTRY_DATA(src, entry); data = _Py_HASHTABLE_ENTRY_DATA(entry);
err = _Py_hashtable_set(dst, key_size, err = _Py_hashtable_set(dst, entry->key, data, src->data_size);
_Py_HASHTABLE_ENTRY_KEY(entry),
data_size, data);
} }
if (err) { if (err) {
_Py_hashtable_destroy(dst); _Py_hashtable_destroy(dst);

View File

@ -20,31 +20,26 @@ typedef struct {
/* used by _Py_hashtable_t.buckets to link entries */ /* used by _Py_hashtable_t.buckets to link entries */
_Py_slist_item_t _Py_slist_item; _Py_slist_item_t _Py_slist_item;
const void *key;
Py_uhash_t key_hash; Py_uhash_t key_hash;
/* data follows */ /* data follows */
} _Py_hashtable_entry_t; } _Py_hashtable_entry_t;
#define _Py_HASHTABLE_ENTRY_KEY(ENTRY) \ #define _Py_HASHTABLE_ENTRY_DATA(ENTRY) \
((void *)((char *)(ENTRY) + sizeof(_Py_hashtable_entry_t))) ((char *)(ENTRY) + sizeof(_Py_hashtable_entry_t))
#define _Py_HASHTABLE_ENTRY_DATA(TABLE, ENTRY) \ #define _Py_HASHTABLE_ENTRY_DATA_AS_VOID_P(ENTRY) \
((char *)(ENTRY) + sizeof(_Py_hashtable_entry_t) + (TABLE)->key_size) (*(void **)_Py_HASHTABLE_ENTRY_DATA(ENTRY))
#define _Py_HASHTABLE_ENTRY_DATA_AS_VOID_P(TABLE, ENTRY) \
(*(void **)_Py_HASHTABLE_ENTRY_DATA(TABLE, ENTRY))
#define _Py_HASHTABLE_ENTRY_READ_DATA(TABLE, DATA, DATA_SIZE, ENTRY) \ #define _Py_HASHTABLE_ENTRY_READ_DATA(TABLE, DATA, DATA_SIZE, ENTRY) \
do { \ do { \
assert((DATA_SIZE) == (TABLE)->data_size); \ assert((DATA_SIZE) == (TABLE)->data_size); \
memcpy(DATA, _Py_HASHTABLE_ENTRY_DATA(TABLE, ENTRY), DATA_SIZE); \ memcpy(DATA, _Py_HASHTABLE_ENTRY_DATA(ENTRY), DATA_SIZE); \
} while (0) } while (0)
typedef Py_uhash_t (*_Py_hashtable_hash_func) (size_t key_size, typedef Py_uhash_t (*_Py_hashtable_hash_func) (const void *key);
const void *pkey); typedef int (*_Py_hashtable_compare_func) (const void *key, const _Py_hashtable_entry_t *he);
typedef int (*_Py_hashtable_compare_func) (size_t key_size,
const void *pkey,
const _Py_hashtable_entry_t *he);
typedef void* (*_Py_hashtable_copy_data_func)(void *data); typedef void* (*_Py_hashtable_copy_data_func)(void *data);
typedef void (*_Py_hashtable_free_data_func)(void *data); typedef void (*_Py_hashtable_free_data_func)(void *data);
typedef size_t (*_Py_hashtable_get_data_size_func)(void *data); typedef size_t (*_Py_hashtable_get_data_size_func)(void *data);
@ -61,7 +56,6 @@ typedef struct {
size_t num_buckets; size_t num_buckets;
size_t entries; /* Total number of entries in the table. */ size_t entries; /* Total number of entries in the table. */
_Py_slist_t *buckets; _Py_slist_t *buckets;
size_t key_size;
size_t data_size; size_t data_size;
_Py_hashtable_hash_func hash_func; _Py_hashtable_hash_func hash_func;
@ -73,21 +67,15 @@ typedef struct {
} _Py_hashtable_t; } _Py_hashtable_t;
/* hash and compare functions for integers and pointers */ /* hash and compare functions for integers and pointers */
PyAPI_FUNC(Py_uhash_t) _Py_hashtable_hash_ptr( PyAPI_FUNC(Py_uhash_t) _Py_hashtable_hash_ptr(const void *key);
size_t key_size, PyAPI_FUNC(Py_uhash_t) _Py_hashtable_hash_int(const void *key);
const void *pkey); PyAPI_FUNC(int) _Py_hashtable_compare_direct(const void *key, const _Py_hashtable_entry_t *entry);
PyAPI_FUNC(int) _Py_hashtable_compare_direct(
size_t key_size,
const void *pkey,
const _Py_hashtable_entry_t *entry);
PyAPI_FUNC(_Py_hashtable_t *) _Py_hashtable_new( PyAPI_FUNC(_Py_hashtable_t *) _Py_hashtable_new(
size_t key_size,
size_t data_size, size_t data_size,
_Py_hashtable_hash_func hash_func, _Py_hashtable_hash_func hash_func,
_Py_hashtable_compare_func compare_func); _Py_hashtable_compare_func compare_func);
PyAPI_FUNC(_Py_hashtable_t *) _Py_hashtable_new_full( PyAPI_FUNC(_Py_hashtable_t *) _Py_hashtable_new_full(
size_t key_size,
size_t data_size, size_t data_size,
size_t init_size, size_t init_size,
_Py_hashtable_hash_func hash_func, _Py_hashtable_hash_func hash_func,
@ -100,65 +88,40 @@ PyAPI_FUNC(_Py_hashtable_t *) _Py_hashtable_copy(_Py_hashtable_t *src);
PyAPI_FUNC(void) _Py_hashtable_clear(_Py_hashtable_t *ht); PyAPI_FUNC(void) _Py_hashtable_clear(_Py_hashtable_t *ht);
PyAPI_FUNC(void) _Py_hashtable_destroy(_Py_hashtable_t *ht); PyAPI_FUNC(void) _Py_hashtable_destroy(_Py_hashtable_t *ht);
typedef int (*_Py_hashtable_foreach_func) (_Py_hashtable_t *ht, typedef int (*_Py_hashtable_foreach_func) (_Py_hashtable_entry_t *entry, void *arg);
_Py_hashtable_entry_t *entry,
void *arg);
PyAPI_FUNC(int) _Py_hashtable_foreach( PyAPI_FUNC(int) _Py_hashtable_foreach(
_Py_hashtable_t *ht, _Py_hashtable_t *ht,
_Py_hashtable_foreach_func func, _Py_hashtable_foreach_func func, void *arg);
void *arg);
PyAPI_FUNC(size_t) _Py_hashtable_size(_Py_hashtable_t *ht); PyAPI_FUNC(size_t) _Py_hashtable_size(_Py_hashtable_t *ht);
PyAPI_FUNC(_Py_hashtable_entry_t*) _Py_hashtable_get_entry( PyAPI_FUNC(_Py_hashtable_entry_t*) _Py_hashtable_get_entry(
_Py_hashtable_t *ht, _Py_hashtable_t *ht,
size_t key_size, const void *key);
const void *pkey);
/* Don't call directly this function,
but use _Py_HASHTABLE_SET() and _Py_HASHTABLE_SET_NODATA() macros */
PyAPI_FUNC(int) _Py_hashtable_set( PyAPI_FUNC(int) _Py_hashtable_set(
_Py_hashtable_t *ht, _Py_hashtable_t *ht,
size_t key_size, const void *key,
const void *pkey, void *data,
size_t data_size, size_t data_size);
void *data);
/* Don't call directly this function, but use _Py_HASHTABLE_GET() macro */
PyAPI_FUNC(int) _Py_hashtable_get( PyAPI_FUNC(int) _Py_hashtable_get(
_Py_hashtable_t *ht, _Py_hashtable_t *ht,
size_t key_size, const void *key,
const void *pkey, void *data,
size_t data_size, size_t data_size);
void *data);
/* Don't call directly this function, but use _Py_HASHTABLE_POP() macro */
PyAPI_FUNC(int) _Py_hashtable_pop( PyAPI_FUNC(int) _Py_hashtable_pop(
_Py_hashtable_t *ht, _Py_hashtable_t *ht,
size_t key_size, const void *key,
const void *pkey, void *data,
size_t data_size, size_t data_size);
void *data);
PyAPI_FUNC(void) _Py_hashtable_delete( PyAPI_FUNC(void) _Py_hashtable_delete(
_Py_hashtable_t *ht, _Py_hashtable_t *ht,
size_t key_size, const void *key);
const void *pkey);
#define _Py_HASHTABLE_SET(TABLE, KEY, DATA) \ #define _Py_HASHTABLE_SET(TABLE, KEY, DATA) \
_Py_hashtable_set(TABLE, sizeof(KEY), &KEY, sizeof(DATA), &(DATA)) _Py_hashtable_set(TABLE, KEY, &(DATA), sizeof(DATA))
#define _Py_HASHTABLE_SET_NODATA(TABLE, KEY) \
_Py_hashtable_set(TABLE, sizeof(KEY), &KEY, 0, NULL)
#define _Py_HASHTABLE_GET_ENTRY(TABLE, KEY) \
_Py_hashtable_get_entry(TABLE, sizeof(KEY), &(KEY))
#define _Py_HASHTABLE_GET(TABLE, KEY, DATA) \ #define _Py_HASHTABLE_GET(TABLE, KEY, DATA) \
_Py_hashtable_get(TABLE, sizeof(KEY), &(KEY), sizeof(DATA), &(DATA)) _Py_hashtable_get(TABLE, KEY, &(DATA), sizeof(DATA))
#define _Py_HASHTABLE_POP(TABLE, KEY, DATA) \
_Py_hashtable_pop(TABLE, sizeof(KEY), &(KEY), sizeof(DATA), &(DATA))
#endif /* Py_LIMITED_API */ #endif /* Py_LIMITED_API */

View File

@ -263,7 +263,7 @@ w_ref(PyObject *v, char *flag, WFILE *p)
if (Py_REFCNT(v) == 1) if (Py_REFCNT(v) == 1)
return 0; return 0;
entry = _Py_HASHTABLE_GET_ENTRY(p->hashtable, v); entry = _Py_hashtable_get_entry(p->hashtable, v);
if (entry != NULL) { if (entry != NULL) {
/* write the reference index to the stream */ /* write the reference index to the stream */
_Py_HASHTABLE_ENTRY_READ_DATA(p->hashtable, &w, sizeof(w), entry); _Py_HASHTABLE_ENTRY_READ_DATA(p->hashtable, &w, sizeof(w), entry);
@ -571,8 +571,7 @@ static int
w_init_refs(WFILE *wf, int version) w_init_refs(WFILE *wf, int version)
{ {
if (version >= 3) { if (version >= 3) {
wf->hashtable = _Py_hashtable_new(sizeof(void *), sizeof(int), wf->hashtable = _Py_hashtable_new(sizeof(int), _Py_hashtable_hash_ptr,
_Py_hashtable_hash_ptr,
_Py_hashtable_compare_direct); _Py_hashtable_compare_direct);
if (wf->hashtable == NULL) { if (wf->hashtable == NULL) {
PyErr_NoMemory(); PyErr_NoMemory();
@ -583,11 +582,9 @@ w_init_refs(WFILE *wf, int version)
} }
static int static int
w_decref_entry(_Py_hashtable_t *ht, _Py_hashtable_entry_t *entry, void *Py_UNUSED(data)) w_decref_entry(_Py_hashtable_entry_t *entry, void *Py_UNUSED(data))
{ {
void *entry_key = *(void **)_Py_HASHTABLE_ENTRY_KEY(entry); Py_XDECREF(entry->key);
assert(ht->key_size == sizeof(entry_key));
Py_XDECREF(entry_key);
return 0; return 0;
} }