| /* | |
| * 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_byteswap.h" // _Py_bswap32() | |
| #include "pycore_initconfig.h" // _Py_GetConfigsAsDict() | |
| #include "pycore_hashtable.h" // _Py_hashtable_new() | |
| #include "pycore_gc.h" // PyGC_Head | |
| #ifdef MS_WINDOWS | |
| #include <windows.h> | |
| static int | |
| _add_windows_config(PyObject *configs) | |
| { | |
| HMODULE hPython3; | |
| wchar_t py3path[MAX_PATH]; | |
| PyObject *dict = PyDict_New(); | |
| PyObject *obj = NULL; | |
| if (!dict) { | |
| return -1; | |
| } | |
| hPython3 = GetModuleHandleW(PY3_DLLNAME); | |
| if (hPython3 && GetModuleFileNameW(hPython3, py3path, MAX_PATH)) { | |
| obj = PyUnicode_FromWideChar(py3path, -1); | |
| } else { | |
| obj = Py_None; | |
| Py_INCREF(obj); | |
| } | |
| if (obj && | |
| !PyDict_SetItemString(dict, "python3_dll", obj) && | |
| !PyDict_SetItemString(configs, "windows", dict)) { | |
| Py_DECREF(obj); | |
| Py_DECREF(dict); | |
| return 0; | |
| } | |
| Py_DECREF(obj); | |
| Py_DECREF(dict); | |
| return -1; | |
| } | |
| #endif | |
| static PyObject * | |
| get_configs(PyObject *self, PyObject *Py_UNUSED(args)) | |
| { | |
| PyObject *dict = _Py_GetConfigsAsDict(); | |
| #ifdef MS_WINDOWS | |
| if (dict) { | |
| if (_add_windows_config(dict) < 0) { | |
| Py_CLEAR(dict); | |
| } | |
| } | |
| #endif | |
| return dict; | |
| } | |
| 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; | |
| } | |
| #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_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; | |
| } |