@@ -74,8 +74,10 @@ chpl_ARRAY_TYPES(chpl_MAKE_ENUM)
74
74
DATATYPE* data; \
75
75
Py_ssize_t size; \
76
76
chpl_bool isOwned; \
77
+ Py_ssize_t numExports; \
77
78
PyObject* eltType; \
78
79
Py_ssize_t ndim; \
80
+ Py_ssize_t* shape; \
79
81
} Array##NAMESUFFIX##Object; \
80
82
PyTypeObject* Array##NAMESUFFIX##Type = NULL;
81
83
chpl_ARRAY_TYPES (chpl_MAKE_ARRAY_STRUCT )
@@ -87,6 +89,7 @@ static int ArrayGenericObject_init(PyObject* self, PyObject* args, PyObject* kwa
87
89
#define chpl_MAKE_DEALLOC (DATATYPE , CHAPELDATATYPE , NAMESUFFIX , ...) \
88
90
static void Array##NAMESUFFIX##Object_dealloc(Array##NAMESUFFIX##Object* self) { \
89
91
if (self->isOwned) chpl_mem_free(self->data, 0, 0); \
92
+ chpl_mem_free(self->shape, 0, 0); \
90
93
Py_CLEAR(self->eltType); \
91
94
void (*tp_free)(PyObject*) = (void (*)(PyObject*))(PyType_GetSlot(Array##NAMESUFFIX##Type, Py_tp_free)); \
92
95
if (!tp_free) PyErr_SetString(PyExc_RuntimeError, "Could not free object"); \
@@ -98,14 +101,19 @@ chpl_ARRAY_TYPES(chpl_MAKE_DEALLOC)
98
101
99
102
#define chpl_MAKE_REPR (DATATYPE , CHAPELDATATYPE , NAMESUFFIX , ...) \
100
103
static PyObject* Array##NAMESUFFIX##Object_repr(Array##NAMESUFFIX##Object* self) { \
101
- return PyUnicode_FromFormat("Array(eltType=" CHAPELDATATYPE ",size=%zd)", self->size); \
104
+ /* TODO: print the shape */ \
105
+ return PyUnicode_FromFormat ("Array(eltType=" CHAPELDATATYPE ",size=%zd,ndim=%zd)" , self -> size , self -> ndim ); \
102
106
}
103
107
chpl_ARRAY_TYPES (chpl_MAKE_REPR )
104
108
#undef chpl_MAKE_REPR
105
109
106
110
// TODO: how can we remove the error checking here with --no-checks or -scheckExceptions=false?
107
111
#define chpl_MAKE_SETITEM (DATATYPE , CHAPELDATATYPE , NAMESUFFIX , CHECKTYPE , ASTYPE , ...) \
108
112
static int Array##NAMESUFFIX##Object_sq_setitem(Array##NAMESUFFIX##Object* self, Py_ssize_t index, PyObject* value) { \
113
+ if (self->ndim != 1) { \
114
+ PyErr_SetString(PyExc_TypeError, "can only assign to 1D arrays"); \
115
+ return -1; \
116
+ } \
109
117
if (!value) { \
110
118
PyErr_SetString(PyExc_TypeError, "cannot delete items from this array"); \
111
119
return -1; \
@@ -128,6 +136,10 @@ chpl_ARRAY_TYPES(chpl_MAKE_SETITEM)
128
136
129
137
#define chpl_MAKE_SETITEM (DATATYPE , CHAPELDATATYPE , NAMESUFFIX , CHECKTYPE , ASTYPE , ...) \
130
138
static int Array##NAMESUFFIX##Object_mp_setitem(Array##NAMESUFFIX##Object* self, PyObject* indexObj, PyObject* value) { \
139
+ if (self->ndim != 1) { \
140
+ PyErr_SetString(PyExc_TypeError, "can only assign to 1D arrays"); \
141
+ return -1; \
142
+ } \
131
143
if (!value) { \
132
144
PyErr_SetString(PyExc_TypeError, "cannot delete items from this array"); \
133
145
return -1; \
@@ -156,6 +168,10 @@ chpl_ARRAY_TYPES(chpl_MAKE_SETITEM)
156
168
157
169
#define chpl_MAKE_GETITEM (DATATYPE , CHAPELDATATYPE , NAMESUFFIX , CHECKTYPE , ASTYPE , FROMTYPE , ...) \
158
170
static PyObject* Array##NAMESUFFIX##Object_sq_getitem(Array##NAMESUFFIX##Object* self, Py_ssize_t index) { \
171
+ if (self->ndim != 1) { \
172
+ PyErr_SetString(PyExc_TypeError, "can only get items from 1D arrays"); \
173
+ return NULL; \
174
+ } \
159
175
if (index < 0 || index >= self->size) { \
160
176
PyErr_SetString(PyExc_IndexError, "index out of bounds"); \
161
177
return NULL; \
@@ -166,6 +182,10 @@ chpl_ARRAY_TYPES(chpl_MAKE_GETITEM)
166
182
#undef chpl_MAKE_GETITEM
167
183
#define chpl_MAKE_GETITEM (DATATYPE , CHAPELDATATYPE , NAMESUFFIX , CHECKTYPE , ASTYPE , FROMTYPE , ...) \
168
184
static PyObject* Array##NAMESUFFIX##Object_mp_getitem(Array##NAMESUFFIX##Object* self, PyObject* indexObj) { \
185
+ if (self->ndim != 1) { \
186
+ PyErr_SetString(PyExc_TypeError, "can only get items from 1D arrays"); \
187
+ return NULL; \
188
+ } \
169
189
if (!PyLong_Check(indexObj)) { \
170
190
/* TODO: support slices */ \
171
191
PyErr_SetString (PyExc_TypeError , "index must be an integer" ); \
@@ -183,6 +203,10 @@ chpl_ARRAY_TYPES(chpl_MAKE_GETITEM)
183
203
184
204
#define chpl_MAKE_LENGTH (DATATYPE , CHAPELDATATYPE , NAMESUFFIX , ...) \
185
205
static Py_ssize_t Array##NAMESUFFIX##Object_length(Array##NAMESUFFIX##Object* self) { \
206
+ if (self->ndim != 1) { \
207
+ PyErr_SetString(PyExc_TypeError, "can only get the length of 1D arrays"); \
208
+ return -1; \
209
+ } \
186
210
return self->size; \
187
211
}
188
212
chpl_ARRAY_TYPES (chpl_MAKE_LENGTH )
@@ -202,27 +226,64 @@ static int Array##NAMESUFFIX##Object_bf_getbuffer(Array##NAMESUFFIX##Object* arr
202
226
view->obj = NULL; \
203
227
return -1; \
204
228
} \
205
- /* other error checking for invalid request in flags?! */ \
229
+ assert(arr->ndim != 0); \
230
+ /* check for any unsupported flags */ \
231
+ if (flags & ~(PyBUF_SIMPLE | PyBUF_WRITABLE | PyBUF_FORMAT | \
232
+ PyBUF_ND | PyBUF_STRIDES | PyBUF_INDIRECT | PyBUF_C_CONTIGUOUS )) { \
233
+ PyErr_Format (PyExc_BufferError , "Unsupported buffer flags: 0x%x" , flags ); \
234
+ view -> obj = NULL ; \
235
+ return -1 ; \
236
+ } \
206
237
view -> buf = arr -> data ; \
207
- view -> obj = ( PyObject * ) arr ; \
238
+ view -> internal = 0 ; \
208
239
view -> len = arr -> size * sizeof (DATATYPE ); \
209
240
view -> itemsize = sizeof (DATATYPE ); \
210
- view -> readonly = 0 ; \
241
+ view -> readonly = 0 ; /* the buffer is always writeable */ \
211
242
view -> ndim = arr -> ndim ; \
212
- view -> format = NULL ; \
213
- if (flags & PyBUF_FORMAT ) { \
214
- view -> format = (char * )FORMATSTRING ; \
243
+ if (flags & PyBUF_FORMAT ) { view -> format = (char * )FORMATSTRING ; } \
244
+ else { view -> format = NULL ; } \
245
+ if (flags & PyBUF_ND ) { view -> shape = arr -> shape ; } \
246
+ else { view -> shape = NULL ; } \
247
+ if (flags & PyBUF_STRIDES ) { \
248
+ view -> strides = (Py_ssize_t * )chpl_mem_alloc (sizeof (Py_ssize_t ) * arr -> ndim , 0 , 0 , 0 ); \
249
+ if (!view -> strides ) { \
250
+ PyErr_SetString (PyExc_MemoryError , "Could not allocate strides" ); \
251
+ view -> obj = NULL ; \
252
+ return -1 ; \
253
+ } \
254
+ if (arr -> ndim == 1 ) { \
255
+ view -> strides [0 ] = sizeof (DATATYPE ); \
256
+ } else { \
257
+ view -> strides [arr -> ndim - 1 ] = sizeof (DATATYPE ); \
258
+ for (Py_ssize_t i = arr -> ndim - 2 ; i >= 0 ; i -- ) { \
259
+ view -> strides [i ] = view -> strides [i + 1 ] * arr -> shape [i + 1 ]; \
260
+ } \
261
+ } \
262
+ view -> internal = (void * )((intptr_t )view -> internal | 0x1 ); /* set 0x1 if we need to free strides later */ \
215
263
} \
216
- /*TODO: support nd arrays*/ \
217
- view -> shape = NULL ; \
218
- view -> strides = NULL ; \
219
- view -> suboffsets = NULL ; \
264
+ else { view -> strides = NULL ; } \
265
+ view -> suboffsets = NULL ; /*always NULL, even if PyBUF_INDIRECT is set*/ \
266
+ arr -> numExports ++ ; \
267
+ view -> obj = ( PyObject * ) arr ; \
220
268
Py_INCREF (view -> obj ); \
221
269
return 0 ; \
222
270
}
223
271
chpl_ARRAY_TYPES (chpl_MAKE_GET_BUFFER )
224
272
#undef chpl_MAKE_GET_BUFFER
225
273
274
+ #define chpl_MAKE_RELEASE_BUFFER (DATATYPE , CHAPELDATATYPE , NAMESUFFIX , _0 , _1 , _2 , SUPPORTSBUFFERS , FORMATSTRING ) \
275
+ static void Array##NAMESUFFIX##Object_bf_releasebuffer(Array##NAMESUFFIX##Object* arr, Py_buffer* view) { \
276
+ arr->numExports--; \
277
+ if ((intptr_t)view->internal & 0x1) { \
278
+ /* free the strides */ \
279
+ chpl_mem_free (view -> strides , 0 , 0 ); \
280
+ view -> internal = (void * )((intptr_t )view -> internal & ~0x1 ); \
281
+ } \
282
+ }
283
+ chpl_ARRAY_TYPES (chpl_MAKE_RELEASE_BUFFER )
284
+ #undef chpl_MAKE_RELEASE_BUFFER
285
+
286
+
226
287
#if PY_VERSION_HEX >= 0x030a0000 /* Python 3.10 */
227
288
#define chpl_Py_TPFLAGS_SEQUENCE Py_TPFLAGS_SEQUENCE
228
289
#else
@@ -242,6 +303,7 @@ chpl_ARRAY_TYPES(chpl_MAKE_GET_BUFFER)
242
303
{"ndim", Py_T_PYSSIZET, offsetof(Array##NAMESUFFIX##Object, ndim), Py_READONLY, PyDoc_STR("number of dimensions in the array")}, \
243
304
{NULL} /* Sentinel */ \
244
305
}; \
306
+ /* TODO: expose shape as a `PyGetSetDef` */ \
245
307
PyType_Slot slots[] = { \
246
308
{Py_tp_init, (void*) ArrayGenericObject_init}, \
247
309
{Py_tp_dealloc, (void*) Array##NAMESUFFIX##Object_dealloc}, \
@@ -256,6 +318,7 @@ chpl_ARRAY_TYPES(chpl_MAKE_GET_BUFFER)
256
318
{Py_tp_methods, (void*) methods}, \
257
319
{Py_tp_members, (void*) members}, \
258
320
{Py_bf_getbuffer, (void*) Array##NAMESUFFIX##Object_bf_getbuffer}, \
321
+ {Py_bf_releasebuffer, (void*) Array##NAMESUFFIX##Object_bf_releasebuffer}, \
259
322
{0, NULL} \
260
323
}; \
261
324
PyType_Spec spec = { \
@@ -281,7 +344,11 @@ chpl_ARRAY_TYPES(chpl_MAKE_TYPE)
281
344
}
282
345
283
346
#define chpl_CREATE_ARRAY (DATATYPE , CHAPELDATATYPE , NAMESUFFIX , ...) \
284
- PyObject* createArray##NAMESUFFIX(DATATYPE* data, Py_ssize_t size, chpl_bool isOwned) { \
347
+ PyObject* createArray##NAMESUFFIX(DATATYPE* data, \
348
+ Py_ssize_t size, \
349
+ Py_ssize_t ndim, \
350
+ Py_ssize_t* shape, \
351
+ chpl_bool isOwned) { \
285
352
assert(Array##NAMESUFFIX##Type); \
286
353
assert(ArrayTypeEnum); \
287
354
PyObject* objPy = PyObject_CallNoArgs((PyObject *) Array##NAMESUFFIX##Type); \
@@ -290,8 +357,10 @@ chpl_ARRAY_TYPES(chpl_MAKE_TYPE)
290
357
obj->data = data; \
291
358
obj->size = size; \
292
359
obj->isOwned = isOwned; \
360
+ obj->numExports = 0; \
293
361
obj->eltType = PyObject_GetAttrString(ArrayTypeEnum, #NAMESUFFIX); \
294
- obj->ndim = 1; /*TODO: when we support proper ND chapel arrays, set dynamicly */ \
362
+ obj->ndim = ndim; \
363
+ obj->shape = shape; \
295
364
return objPy; \
296
365
}
297
366
chpl_ARRAY_TYPES (chpl_CREATE_ARRAY )
0 commit comments