Conversation
When XXH32_createState() returns NULL, the self object allocated by PyObject_New was leaked. Free it with PyObject_Del before returning NULL.
- Fix memory leak when XXH3_createState() fails (same pattern as XXH32/XXH64) - Explicitly initialize self->seed = 0 for consistency with PYXXH3_128_new and to not rely on zero-initialized memory from PyObject_New
XXH3_128 produces a 128-bit digest, which is 16 bytes, not 8.
PyModuleDef.m_slots is PyModuleDef_Slot * (non-const), so declaring slots as const causes a -Wincompatible-pointer-types-discards-qualifiers warning. Remove the const qualifier.
There was a problem hiding this comment.
Code Review
This pull request implements memory cleanup using PyObject_Del during xxhash state creation failures, corrects the PYXXH3_128 digest documentation, and modifies module slot definitions. The reviewer suggests calling PyErr_NoMemory() when state creation fails to ensure the Python C API correctly signals an out-of-memory error instead of a SystemError.
| } | ||
|
|
||
| if ((self->xxhash_state = XXH32_createState()) == NULL) { | ||
| PyObject_Del(self); |
There was a problem hiding this comment.
When XXH32_createState() fails, it returns NULL without setting a Python exception. In Python's C API, returning NULL from a function without an exception set triggers a SystemError. You should call PyErr_NoMemory() to properly signal the out-of-memory condition.
PyObject_Del(self);
PyErr_NoMemory();| } | ||
|
|
||
| if ((p->xxhash_state = XXH32_createState()) == NULL) { | ||
| PyObject_Del(p); |
There was a problem hiding this comment.
| } | ||
|
|
||
| if ((self->xxhash_state = XXH64_createState()) == NULL) { | ||
| PyObject_Del(self); |
There was a problem hiding this comment.
When XXH64_createState() fails, it returns NULL without setting a Python exception. In Python's C API, returning NULL from a function without an exception set triggers a SystemError. You should call PyErr_NoMemory() to properly signal the out-of-memory condition.
PyObject_Del(self);
PyErr_NoMemory();| } | ||
|
|
||
| if ((p->xxhash_state = XXH64_createState()) == NULL) { | ||
| PyObject_Del(p); |
There was a problem hiding this comment.
| } | ||
|
|
||
| if ((self->xxhash_state = XXH3_createState()) == NULL) { | ||
| PyObject_Del(self); |
There was a problem hiding this comment.
When XXH3_createState() fails, it returns NULL without setting a Python exception. In Python's C API, returning NULL from a function without an exception set triggers a SystemError. You should call PyErr_NoMemory() to properly signal the out-of-memory condition.
PyObject_Del(self);
PyErr_NoMemory();| } | ||
|
|
||
| if ((p->xxhash_state = XXH3_createState()) == NULL) { | ||
| PyObject_Del(p); |
There was a problem hiding this comment.
| } | ||
|
|
||
| if ((self->xxhash_state = XXH3_createState()) == NULL) { | ||
| PyObject_Del(self); |
There was a problem hiding this comment.
When XXH3_createState() fails, it returns NULL without setting a Python exception. In Python's C API, returning NULL from a function without an exception set triggers a SystemError. You should call PyErr_NoMemory() to properly signal the out-of-memory condition.
PyObject_Del(self);
PyErr_NoMemory();| } | ||
|
|
||
| if ((p->xxhash_state = XXH3_createState()) == NULL) { | ||
| PyObject_Del(p); |
There was a problem hiding this comment.
Returning NULL from a Python C API function without setting an exception causes CPython to raise a SystemError. When XXH*_createState() returns NULL (out of memory), we now call PyErr_NoMemory() before returning NULL in all 8 _new and _copy functions.
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request improves error handling in src/_xxhash.c by adding memory cleanup and reporting when state creation fails. It also corrects the documentation for the 128-bit digest size, ensures the seed is initialized in PYXXH3_64_new, and modifies the module slots definition. The review feedback highlights a consistent improvement opportunity: using Py_DECREF instead of PyObject_Del in failure paths to ensure that Python's reference counting and object deallocation are handled correctly.
| PyObject_Del(self); | ||
| PyErr_NoMemory(); | ||
| return NULL; |
There was a problem hiding this comment.
Using PyObject_Del directly on a newly created object bypasses the standard reference counting mechanism. While it frees the memory, it doesn't handle the type reference count correctly if the type is a heap type or if Python's internal logic expects a full deallocation cycle. It is better to use Py_DECREF(self), which will trigger the object's tp_dealloc (PYXXH32_dealloc). Since self->xxhash_state is set to NULL on failure, the destructor is safe to call.
Py_DECREF(self);
return PyErr_NoMemory();| PyObject_Del(p); | ||
| PyErr_NoMemory(); | ||
| return NULL; |
| PyObject_Del(self); | ||
| PyErr_NoMemory(); | ||
| return NULL; |
| PyObject_Del(p); | ||
| PyErr_NoMemory(); | ||
| return NULL; |
| PyObject_Del(self); | ||
| PyErr_NoMemory(); | ||
| return NULL; |
| PyObject_Del(p); | ||
| PyErr_NoMemory(); | ||
| return NULL; |
| PyObject_Del(self); | ||
| PyErr_NoMemory(); | ||
| return NULL; |
| PyObject_Del(p); | ||
| PyErr_NoMemory(); | ||
| return NULL; |
Py_DECREF is the idiomatic CPython pattern for cleaning up a newly allocated object in tp_new/tp_copy error paths — it calls tp_dealloc which properly integrates with Python's refcounting and GC. To make this safe, all four dealloc functions now NULL-check self->xxhash_state before calling XXH*_freeState(), so they handle partially-initialized objects where state creation failed.
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request improves memory management and error handling in the C extension by adding Py_DECREF and PyErr_NoMemory calls during state creation failures, along with null checks in deallocators. It also corrects the xxh3_128 digest documentation and increments the package version. Review feedback highlights the need for consistent seed initialization and state resets in the tp_new handlers for xxh32 and xxh64 to ensure objects are in a valid state even if init is bypassed.
| Py_DECREF(self); | ||
| PyErr_NoMemory(); |
There was a problem hiding this comment.
While fixing the memory leak and setting the error indicator is correct, the xxh32 object is still left in an inconsistent state if __init__ is not called (e.g., when subclassing without calling super().__init__ or using __new__ directly). For consistency with the xxh3_64 and xxh3_128 implementations in this file, you should also initialize self->seed = 0 and call XXH32_reset(self->xxhash_state, 0) after the state is successfully created.
| Py_DECREF(self); | ||
| PyErr_NoMemory(); |
There was a problem hiding this comment.
Similar to the xxh32 implementation, the xxh64 object should have its seed initialized to 0 and its state reset using XXH64_reset(self->xxhash_state, 0) in the tp_new handler. This ensures the object is in a valid state even if __init__ is bypassed, which is important for methods like copy() or update() that might be called on an uninitialized object.
For consistency with XXH3_64 and XXH3_128 implementations, tp_new now initializes self->seed = 0 and resets the internal state. This ensures the object is in a valid state even if __init__ is bypassed (e.g. via __new__ directly or subclassing without calling super().__init__).
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request enhances the C extension's stability by implementing proper reference counting and error handling during state allocation failures. Key changes include adding null checks in deallocators to prevent potential crashes, updating the XXH3_128 docstring to correctly specify a 16-byte digest, and adjusting the module slots' type to avoid compiler warnings. The version has also been updated to 3.8.0.dev3. I have no feedback to provide.
No description provided.