cpython/Modules/_testinternalcapi.c

237 lines
5.9 KiB
C

/*
* C Extension module to test Python internal C APIs (Include/internal).
*/
#if !defined(Py_BUILD_CORE_BUILTIN) && !defined(Py_BUILD_CORE_MODULE)
# error "Py_BUILD_CORE_BUILTIN or Py_BUILD_CORE_MODULE must be defined"
#endif
/* Always enable assertions */
#undef NDEBUG
#define PY_SSIZE_T_CLEAN
#include "Python.h"
#include "pycore_bitutils.h" // _Py_bswap32()
#include "pycore_initconfig.h" // _Py_GetConfigsAsDict()
#include "pycore_hashtable.h" // _Py_hashtable_new()
#include "pycore_gc.h" // PyGC_Head
static PyObject *
get_configs(PyObject *self, PyObject *Py_UNUSED(args))
{
return _Py_GetConfigsAsDict();
}
static PyObject*
get_recursion_depth(PyObject *self, PyObject *Py_UNUSED(args))
{
PyThreadState *tstate = PyThreadState_Get();
/* subtract one to ignore the frame of the get_recursion_depth() call */
return PyLong_FromLong(tstate->recursion_depth - 1);
}
static PyObject*
test_bswap(PyObject *self, PyObject *Py_UNUSED(args))
{
uint16_t u16 = _Py_bswap16(UINT16_C(0x3412));
if (u16 != UINT16_C(0x1234)) {
PyErr_Format(PyExc_AssertionError,
"_Py_bswap16(0x3412) returns %u", u16);
return NULL;
}
uint32_t u32 = _Py_bswap32(UINT32_C(0x78563412));
if (u32 != UINT32_C(0x12345678)) {
PyErr_Format(PyExc_AssertionError,
"_Py_bswap32(0x78563412) returns %lu", u32);
return NULL;
}
uint64_t u64 = _Py_bswap64(UINT64_C(0xEFCDAB9078563412));
if (u64 != UINT64_C(0x1234567890ABCDEF)) {
PyErr_Format(PyExc_AssertionError,
"_Py_bswap64(0xEFCDAB9078563412) returns %llu", u64);
return NULL;
}
Py_RETURN_NONE;
}
static int
check_popcount(uint32_t x, int expected)
{
// Use volatile to prevent the compiler to optimize out the whole test
volatile uint32_t u = x;
int bits = _Py_popcount32(u);
if (bits != expected) {
PyErr_Format(PyExc_AssertionError,
"_Py_popcount32(%lu) returns %i, expected %i",
(unsigned long)x, bits, expected);
return -1;
}
return 0;
}
static PyObject*
test_popcount(PyObject *self, PyObject *Py_UNUSED(args))
{
#define CHECK(X, RESULT) \
do { \
if (check_popcount(X, RESULT) < 0) { \
return NULL; \
} \
} while (0)
CHECK(0, 0);
CHECK(1, 1);
CHECK(0x08080808, 4);
CHECK(0x10101010, 4);
CHECK(0x10204080, 4);
CHECK(0xDEADCAFE, 22);
CHECK(0xFFFFFFFF, 32);
Py_RETURN_NONE;
#undef CHECK
}
#define TO_PTR(ch) ((void*)(uintptr_t)ch)
#define FROM_PTR(ptr) ((uintptr_t)ptr)
#define VALUE(key) (1 + ((int)(key) - 'a'))
static Py_uhash_t
hash_char(const void *key)
{
char ch = (char)FROM_PTR(key);
return ch;
}
static int
hashtable_cb(_Py_hashtable_t *table,
const void *key_ptr, const void *value_ptr,
void *user_data)
{
int *count = (int *)user_data;
char key = (char)FROM_PTR(key_ptr);
int value = (int)FROM_PTR(value_ptr);
assert(value == VALUE(key));
*count += 1;
return 0;
}
static PyObject*
test_hashtable(PyObject *self, PyObject *Py_UNUSED(args))
{
_Py_hashtable_t *table = _Py_hashtable_new(hash_char,
_Py_hashtable_compare_direct);
if (table == NULL) {
return PyErr_NoMemory();
}
// Using an newly allocated table must not crash
assert(table->nentries == 0);
assert(table->nbuckets > 0);
assert(_Py_hashtable_get(table, TO_PTR('x')) == NULL);
// Test _Py_hashtable_set()
char key;
for (key='a'; key <= 'z'; key++) {
int value = VALUE(key);
if (_Py_hashtable_set(table, TO_PTR(key), TO_PTR(value)) < 0) {
_Py_hashtable_destroy(table);
return PyErr_NoMemory();
}
}
assert(table->nentries == 26);
assert(table->nbuckets > table->nentries);
// Test _Py_hashtable_get_entry()
for (key='a'; key <= 'z'; key++) {
_Py_hashtable_entry_t *entry = _Py_hashtable_get_entry(table, TO_PTR(key));
assert(entry != NULL);
assert(entry->key = TO_PTR(key));
assert(entry->value = TO_PTR(VALUE(key)));
}
// Test _Py_hashtable_get()
for (key='a'; key <= 'z'; key++) {
void *value_ptr = _Py_hashtable_get(table, TO_PTR(key));
assert((int)FROM_PTR(value_ptr) == VALUE(key));
}
// Test _Py_hashtable_steal()
key = 'p';
void *value_ptr = _Py_hashtable_steal(table, TO_PTR(key));
assert((int)FROM_PTR(value_ptr) == VALUE(key));
assert(table->nentries == 25);
assert(_Py_hashtable_get_entry(table, TO_PTR(key)) == NULL);
// Test _Py_hashtable_foreach()
int count = 0;
int res = _Py_hashtable_foreach(table, hashtable_cb, &count);
assert(res == 0);
assert(count == 25);
// Test _Py_hashtable_clear()
_Py_hashtable_clear(table);
assert(table->nentries == 0);
assert(table->nbuckets > 0);
assert(_Py_hashtable_get(table, TO_PTR('x')) == NULL);
_Py_hashtable_destroy(table);
Py_RETURN_NONE;
}
static PyMethodDef TestMethods[] = {
{"get_configs", get_configs, METH_NOARGS},
{"get_recursion_depth", get_recursion_depth, METH_NOARGS},
{"test_bswap", test_bswap, METH_NOARGS},
{"test_popcount", test_popcount, METH_NOARGS},
{"test_hashtable", test_hashtable, METH_NOARGS},
{NULL, NULL} /* sentinel */
};
static struct PyModuleDef _testcapimodule = {
PyModuleDef_HEAD_INIT,
"_testinternalcapi",
NULL,
-1,
TestMethods,
NULL,
NULL,
NULL,
NULL
};
PyMODINIT_FUNC
PyInit__testinternalcapi(void)
{
PyObject *module = PyModule_Create(&_testcapimodule);
if (module == NULL) {
return NULL;
}
if (PyModule_AddObject(module, "SIZEOF_PYGC_HEAD",
PyLong_FromSsize_t(sizeof(PyGC_Head))) < 0) {
goto error;
}
return module;
error:
Py_DECREF(module);
return NULL;
}