@@ -118,20 +118,31 @@ NumPy C-API functions
118118
119119 typedef struct {
120120 char name[128]; /* multiple of 64 to keep the struct aligned */
121- PyDataMem_AllocFunc *alloc;
122- PyDataMem_ZeroedAllocFunc *zeroed_alloc;
123- PyDataMem_FreeFunc *free;
124- PyDataMem_ReallocFunc *realloc;
121+ PyDataMemAllocator allocator;
125122 } PyDataMem_Handler;
126123
127- where the function's signatures are
124+ where the allocator structure is
128125
129126 .. code-block :: c
130127
131- typedef void *(PyDataMem_AllocFunc)(size_t size);
132- typedef void *(PyDataMem_ZeroedAllocFunc)(size_t nelems, size_t elsize);
133- typedef void (PyDataMem_FreeFunc)(void *ptr, size_t size);
134- typedef void *(PyDataMem_ReallocFunc)(void *ptr, size_t size);
128+ /* The declaration of free differs from PyMemAllocatorEx */
129+ typedef struct {
130+ void *ctx;
131+ void* (*malloc) (void *ctx, size_t size);
132+ void* (*calloc) (void *ctx, size_t nelem, size_t elsize);
133+ void* (*realloc) (void *ctx, void *ptr, size_t new_size);
134+ void (*free) (void *ctx, void *ptr, size_t size);
135+ } PyDataMemAllocator;
136+
137+ The use of a ``size `` parameter in ``free `` differentiates this struct from
138+ the :c:type: `PyMemAllocatorEx ` struct in Python. This call signature is
139+ used internally in NumPy currently, and also in other places for instance
140+ `C++98 <https://en.cppreference.com/w/cpp/memory/allocator/deallocate> `,
141+ `C++11 <https://en.cppreference.com/w/cpp/memory/allocator_traits/deallocate> `, and
142+ `Rust (allocator_api) <https://doc.rust-lang.org/std/alloc/trait.Allocator.html#tymethod.deallocate> `.
143+
144+ The consumer of the `PyDataMemAllocator ` interface must keep track of ``size `` and make sure it is
145+ consistent with the parameter passed to the ``(m|c|re)alloc `` functions.
135146
136147.. c :function :: const PyDataMem_Handler * PyDataMem_SetHandler (PyDataMem_Handler *handler)
137148
@@ -162,8 +173,16 @@ the ``sz`` argument is correct.
162173 #include <numpy/arrayobject.h>
163174 NPY_NO_EXPORT void *
164175
165- shift_alloc(size_t sz) {
166- char *real = (char *)malloc(sz + 64);
176+ typedef struct {
177+ void *(*malloc)(size_t);
178+ void *(*calloc)(size_t, size_t);
179+ void *(*realloc)(void *, size_t);
180+ void (*free)(void *);
181+ } Allocator;
182+
183+ NPY_NO_EXPORT void *
184+ shift_alloc(Allocator *ctx, size_t sz) {
185+ char *real = (char *)ctx->malloc(sz + 64);
167186 if (real == NULL) {
168187 return NULL;
169188 }
@@ -172,8 +191,8 @@ the ``sz`` argument is correct.
172191 }
173192
174193 NPY_NO_EXPORT void *
175- shift_zero(size_t sz, size_t cnt) {
176- char *real = (char *)calloc(sz + 64, cnt);
194+ shift_zero(Allocator *ctx, size_t sz, size_t cnt) {
195+ char *real = (char *)ctx-> calloc(sz + 64, cnt);
177196 if (real == NULL) {
178197 return NULL;
179198 }
@@ -183,45 +202,44 @@ the ``sz`` argument is correct.
183202 }
184203
185204 NPY_NO_EXPORT void
186- shift_free(void * p, npy_uintp sz) {
205+ shift_free(Allocator *ctx, void * p, npy_uintp sz) {
187206 if (p == NULL) {
188207 return ;
189208 }
190209 char *real = (char *)p - 64;
191210 if (strncmp(real, "originally allocated", 20) != 0) {
192211 fprintf(stdout, "uh-oh, unmatched shift_free, "
193212 "no appropriate prefix\\n");
194- /* Make the C runtime crash by calling free on the wrong address */
195- free((char *)p + 10);
196- /* free(real); */
213+ /* Make C runtime crash by calling free on the wrong address */
214+ ctx-> free((char *)p + 10);
215+ /* ctx-> free(real); */
197216 }
198217 else {
199- int i = atoi(real +20);
218+ npy_uintp i = (npy_uintp) atoi(real +20);
200219 if (i != sz) {
201- fprintf(stderr, "uh-oh, unmatched "
202- "shift_free(ptr, %d) but allocated %d\\n", sz, i);
203- /* Make the C runtime crash by calling free on the wrong address */
204- /* free((char *)p + 10); */
205- free(real);
220+ fprintf(stderr, "uh-oh, unmatched shift_free"
221+ "(ptr, %ld) but allocated %ld\\n", sz, i);
222+ /* This happens in some places, only print */
223+ ctx->free(real);
206224 }
207225 else {
208- free(real);
226+ ctx-> free(real);
209227 }
210228 }
211229 }
212230
213231 NPY_NO_EXPORT void *
214- shift_realloc(void * p, npy_uintp sz) {
232+ shift_realloc(Allocator *ctx, void * p, npy_uintp sz) {
215233 if (p != NULL) {
216234 char *real = (char *)p - 64;
217235 if (strncmp(real, "originally allocated", 20) != 0) {
218236 fprintf(stdout, "uh-oh, unmatched shift_realloc\\n");
219237 return realloc(p, sz);
220238 }
221- return (void *)((char *)realloc(real, sz + 64) + 64);
239+ return (void *)((char *)ctx-> realloc(real, sz + 64) + 64);
222240 }
223241 else {
224- char *real = (char *)realloc(p, sz + 64);
242+ char *real = (char *)ctx-> realloc(p, sz + 64);
225243 if (real == NULL) {
226244 return NULL;
227245 }
@@ -231,63 +249,24 @@ the ``sz`` argument is correct.
231249 }
232250 }
233251
234- static PyDataMem_Handler new_handler = {
235- "secret_data_allocator",
236- shift_alloc, /* alloc */
237- shift_zero, /* zeroed_alloc */
238- shift_free, /* free */
239- shift_realloc /* realloc */
252+ static Allocator new_handler_ctx = {
253+ malloc,
254+ calloc,
255+ realloc,
256+ free
240257 };
241258
242- static PyObject* mem_policy_test_prefix(PyObject *self, PyObject *args)
243- {
244-
245- if (!PyArray_Check(args)) {
246- PyErr_SetString(PyExc_ValueError,
247- "must be called with a numpy scalar or ndarray");
259+ static PyDataMem_Handler new_handler = {
260+ "secret_data_allocator",
261+ {
262+ &new_handler_ctx,
263+ shift_alloc, /* malloc */
264+ shift_zero, /* calloc */
265+ shift_realloc, /* realloc */
266+ shift_free /* free */
248267 }
249- return PyUnicode_FromString(
250- PyDataMem_GetHandlerName((PyArrayObject*)args));
251- };
252-
253- static PyObject* mem_policy_set_new_policy(PyObject *self, PyObject *args)
254- {
255-
256- const PyDataMem_Handler *old = PyDataMem_SetHandler(&new_handler);
257- return PyUnicode_FromString(old->name);
258-
259268 };
260-
261- static PyObject* mem_policy_set_old_policy(PyObject *self, PyObject *args)
262- {
263-
264- const PyDataMem_Handler *old = PyDataMem_SetHandler(NULL);
265- return PyUnicode_FromString(old->name);
266-
267- };
268-
269- static PyMethodDef methods[] = {
270- {"test_prefix", (PyCFunction)mem_policy_test_prefix, METH_O},
271- {"set_new_policy", (PyCFunction)mem_policy_set_new_policy, METH_NOARGS},
272- {"set_old_policy", (PyCFunction)mem_policy_set_old_policy, METH_NOARGS},
273- { NULL }
274- };
275-
276- static struct PyModuleDef moduledef = {
277- PyModuleDef_HEAD_INIT,
278- "mem_policy", /* m_name */
279- NULL, /* m_doc */
280- -1, /* m_size */
281- methods, /* m_methods */
282- };
283-
284- PyMODINIT_FUNC
285- PyInit_mem_policy(void) {
286- PyObject *mod = PyModule_Create(&moduledef);
287- import_array();
288- return mod;
289- }
290-
269+ '''
291270
292271 Related Work
293272------------
@@ -315,7 +294,9 @@ mechanism. The PR was merged with no example code for using these features.
315294Discussion
316295----------
317296
318- Not yet discussed on the mailing list.
297+ The discussion on the mailing list led to the ``PyDataMemAllocator `` struct
298+ with a ``context `` field like :c:type: `PyMemAllocatorEx ` but with a different
299+ signature for ``free ``.
319300
320301
321302References and Footnotes
0 commit comments