Repository: numpy/numpy
File: numpy/_core/src/multiarray/arrayobject.c (C)

Code before:
                                "no memory handler found but OWNDATA flag set");
                return -1;
            }
            if (npy_thread_unsafe_state.warn_if_no_mem_policy) {
                char const *msg = "Trying to dealloc data, but a memory policy "
                    "is not set. If you take ownership of the data, you must "
                    "set a base owning the data (e.g. a PyCapsule).";
                retval = WARN_IN_DEALLOC(PyExc_RuntimeWarning, msg, unraisable);
                assert(retval == 0);  // must be true if unraisable.
            }
            // Guess at malloc/free ???
            free(fa->data);
        }
        else {
            size_t nbytes = PyArray_NBYTES(self);
            if (nbytes == 0) {
                nbytes = 1;
            }
            PyDataMem_UserFREE(fa->data, nbytes, fa->mem_handler);
            Py_CLEAR(fa->mem_handler);
        }
        fa->data = NULL;
    }

    /* must match allocation in PyArray_NewFromDescr */
    if (fa->dimensions != NULL) {
        npy_free_cache_dim(fa->dimensions, 2 * fa->nd);
        fa->dimensions = NULL;
    }
    Py_CLEAR(fa->descr);
    return 0;
}

static void
array_dealloc(PyArrayObject *self)
{
    // NPY_TRUE flags that errors are unraisable.
    int ret = _dealloc_all_but_self(self, NPY_TRUE);
    assert(ret == 0);  // should always succeed if unraisable.
    // Only done on actual deallocation, nothing allocated by numpy.
    if (((PyArrayObject_fields *)self)->weakreflist != NULL) {
        PyObject_ClearWeakRefs((PyObject *)self);
    }
    Py_TYPE(self)->tp_free((PyObject *)self);
}

NPY_NO_EXPORT int
dealloc_all_but_self(PyArrayObject *self)
{
    // NPY_FALSE flags that errors can be raised.
    return _dealloc_all_but_self(self, NPY_FALSE);

Review comment (performance):
doesn't `npy_free_cache_dims` include the `!= NULL` check like typical `free` functions?

Code after:
                }
            }
            // Guess at malloc/free ???
            free(fa->data);
        }
        else {
            size_t nbytes = PyArray_NBYTES(self);
            if (nbytes == 0) {
                nbytes = 1;
            }
            PyDataMem_UserFREE(fa->data, nbytes, fa->mem_handler);
            Py_CLEAR(fa->mem_handler);
        }
        fa->data = NULL;
    }

    /* must match allocation in PyArray_NewFromDescr */
    npy_free_cache_dim(fa->dimensions, 2 * fa->nd);
    fa->dimensions = NULL;
    Py_CLEAR(fa->descr);
    return 0;
}

static void
array_dealloc(PyArrayObject *self)
{
    // NPY_TRUE flags that errors are unraisable.
    int ret = _clear_array_attributes(self, NPY_TRUE);
    assert(ret == 0);  // should always succeed if unraisable.
    // Only done on actual deallocation, nothing allocated by numpy.
    if (((PyArrayObject_fields *)self)->weakreflist != NULL) {
        PyObject_ClearWeakRefs((PyObject *)self);
    }
    Py_TYPE(self)->tp_free((PyObject *)self);
}

NPY_NO_EXPORT int
clear_array_attributes(PyArrayObject *self)
{
    // NPY_FALSE flags that errors can be raised.
    return _clear_array_attributes(self, NPY_FALSE);
}

/*NUMPY_API
 * Prints the raw data of the ndarray in a form useful for debugging
 * low-level C issues.
 */
NPY_NO_EXPORT void
PyArray_DebugPrint(PyArrayObject *obj)
{
    int i;