Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes15kdownloads
fortranobject.c1437 linesDownload Raw Back to src
1#define FORTRANOBJECT_C
2#include "fortranobject.h"
3
4#ifdef __cplusplus
5extern "C" {
6#endif
7
8#include <stdarg.h>
9#include <stdlib.h>
10#include <string.h>
11
12/*
13  This file implements: FortranObject, array_from_pyobj, copy_ND_array
14
15  Author: Pearu Peterson <pearu@cens.ioc.ee>
16  $Revision: 1.52 $
17  $Date: 2005/07/11 07:44:20 $
18*/
19
20int
21F2PyDict_SetItemString(PyObject *dict, char *name, PyObject *obj)
22{
23    if (obj == NULL) {
24        fprintf(stderr, "Error loading %s\n", name);
25        if (PyErr_Occurred()) {
26            PyErr_Print();
27            PyErr_Clear();
28        }
29        return -1;
30    }
31    return PyDict_SetItemString(dict, name, obj);
32}
33
34/*
35 * Python-only fallback for thread-local callback pointers
36 */
37void *
38F2PySwapThreadLocalCallbackPtr(char *key, void *ptr)
39{
40    PyObject *local_dict, *value;
41    void *prev;
42
43    local_dict = PyThreadState_GetDict();
44    if (local_dict == NULL) {
45        Py_FatalError(
46                "F2PySwapThreadLocalCallbackPtr: PyThreadState_GetDict "
47                "failed");
48    }
49
50    value = PyDict_GetItemString(local_dict, key); // noqa: borrowed-ref OK
51    if (value != NULL) {
52        prev = PyLong_AsVoidPtr(value);
53        if (PyErr_Occurred()) {
54            Py_FatalError(
55                    "F2PySwapThreadLocalCallbackPtr: PyLong_AsVoidPtr failed");
56        }
57    }
58    else {
59        prev = NULL;
60    }
61
62    value = PyLong_FromVoidPtr((void *)ptr);
63    if (value == NULL) {
64        Py_FatalError(
65                "F2PySwapThreadLocalCallbackPtr: PyLong_FromVoidPtr failed");
66    }
67
68    if (PyDict_SetItemString(local_dict, key, value) != 0) {
69        Py_FatalError(
70                "F2PySwapThreadLocalCallbackPtr: PyDict_SetItemString failed");
71    }
72
73    Py_DECREF(value);
74
75    return prev;
76}
77
78void *
79F2PyGetThreadLocalCallbackPtr(char *key)
80{
81    PyObject *local_dict, *value;
82    void *prev;
83
84    local_dict = PyThreadState_GetDict();
85    if (local_dict == NULL) {
86        Py_FatalError(
87                "F2PyGetThreadLocalCallbackPtr: PyThreadState_GetDict failed");
88    }
89
90    value = PyDict_GetItemString(local_dict, key); // noqa: borrowed-ref OK
91    if (value != NULL) {
92        prev = PyLong_AsVoidPtr(value);
93        if (PyErr_Occurred()) {
94            Py_FatalError(
95                    "F2PyGetThreadLocalCallbackPtr: PyLong_AsVoidPtr failed");
96        }
97    }
98    else {
99        prev = NULL;
100    }
101
102    return prev;
103}
104
105static PyArray_Descr *
106get_descr_from_type_and_elsize(const int type_num, const int elsize)  {
107  PyArray_Descr * descr = PyArray_DescrFromType(type_num);
108  if (type_num == NPY_STRING) {
109    // PyArray_DescrFromType returns descr with elsize = 0.
110    PyArray_DESCR_REPLACE(descr);
111    if (descr == NULL) {
112      return NULL;
113    }
114    PyDataType_SET_ELSIZE(descr, elsize);
115  }
116  return descr;
117}
118
119/************************* FortranObject *******************************/
120
121typedef PyObject *(*fortranfunc)(PyObject *, PyObject *, PyObject *, void *);
122
123PyObject *
124PyFortranObject_New(FortranDataDef *defs, f2py_void_func init)
125{
126    int i;
127    PyFortranObject *fp = NULL;
128    PyObject *v = NULL;
129    if (init != NULL) { /* Initialize F90 module objects */
130        (*(init))();
131    }
132    fp = PyObject_New(PyFortranObject, &PyFortran_Type);
133    if (fp == NULL) {
134        return NULL;
135    }
136    if ((fp->dict = PyDict_New()) == NULL) {
137        Py_DECREF(fp);
138        return NULL;
139    }
140    fp->len = 0;
141    while (defs[fp->len].name != NULL) {
142        fp->len++;
143    }
144    if (fp->len == 0) {
145        goto fail;
146    }
147    fp->defs = defs;
148    for (i = 0; i < fp->len; i++) {
149        if (fp->defs[i].rank == -1) { /* Is Fortran routine */
150            v = PyFortranObject_NewAsAttr(&(fp->defs[i]));
151            if (v == NULL) {
152                goto fail;
153            }
154            PyDict_SetItemString(fp->dict, fp->defs[i].name, v);
155            Py_XDECREF(v);
156        }
157        else if ((fp->defs[i].data) !=
158                 NULL) { /* Is Fortran variable or array (not allocatable) */
159            PyArray_Descr *
160            descr = get_descr_from_type_and_elsize(fp->defs[i].type,
161                                                   fp->defs[i].elsize);
162            if (descr == NULL) {
163                goto fail;
164            }
165            v = PyArray_NewFromDescr(&PyArray_Type, descr, fp->defs[i].rank,
166                                     fp->defs[i].dims.d, NULL, fp->defs[i].data,
167                                     NPY_ARRAY_FARRAY, NULL);
168            if (v == NULL) {
169                Py_DECREF(descr);
170                goto fail;
171            }
172            PyDict_SetItemString(fp->dict, fp->defs[i].name, v);
173            Py_XDECREF(v);
174        }
175    }
176    return (PyObject *)fp;
177fail:
178    Py_XDECREF(fp);
179    return NULL;
180}
181
182PyObject *
183PyFortranObject_NewAsAttr(FortranDataDef *defs)
184{ /* used for calling F90 module routines */
185    PyFortranObject *fp = NULL;
186    fp = PyObject_New(PyFortranObject, &PyFortran_Type);
187    if (fp == NULL)
188        return NULL;
189    if ((fp->dict = PyDict_New()) == NULL) {
190        PyObject_Del(fp);
191        return NULL;
192    }
193    fp->len = 1;
194    fp->defs = defs;
195    if (defs->rank == -1) {
196      PyDict_SetItemString(fp->dict, "__name__", PyUnicode_FromFormat("function %s", defs->name));
197    } else if (defs->rank == 0) {
198      PyDict_SetItemString(fp->dict, "__name__", PyUnicode_FromFormat("scalar %s", defs->name));
199    } else {
200      PyDict_SetItemString(fp->dict, "__name__", PyUnicode_FromFormat("array %s", defs->name));
201    }
202    return (PyObject *)fp;
203}
204
205/* Fortran methods */
206
207static void
208fortran_dealloc(PyFortranObject *fp)
209{
210    Py_XDECREF(fp->dict);
211    PyObject_Del(fp);
212}
213
214/* Returns number of bytes consumed from buf, or -1 on error. */
215static Py_ssize_t
216format_def(char *buf, Py_ssize_t size, FortranDataDef def)
217{
218    char *p = buf;
219    int i;
220    npy_intp n;
221
222    n = PyOS_snprintf(p, size, "array(%" NPY_INTP_FMT, def.dims.d[0]);
223    if (n < 0 || n >= size) {
224        return -1;
225    }
226    p += n;
227    size -= n;
228
229    for (i = 1; i < def.rank; i++) {
230        n = PyOS_snprintf(p, size, ",%" NPY_INTP_FMT, def.dims.d[i]);
231        if (n < 0 || n >= size) {
232            return -1;
233        }
234        p += n;
235        size -= n;
236    }
237
238    if (size <= 0) {
239        return -1;
240    }
241
242    *p++ = ')';
243    size--;
244
245    if (def.data == NULL) {
246        static const char notalloc[] = ", not allocated";
247        if ((size_t)size < sizeof(notalloc)) {
248            return -1;
249        }
250        memcpy(p, notalloc, sizeof(notalloc));
251        p += sizeof(notalloc);
252        size -= sizeof(notalloc);
253    }
254
255    return p - buf;
256}
257
258static PyObject *
259fortran_doc(FortranDataDef def)
260{
261    char *buf, *p;
262    PyObject *s = NULL;
263    Py_ssize_t n, origsize, size = 100;
264
265    if (def.doc != NULL) {
266        size += strlen(def.doc);
267    }
268    origsize = size;
269    buf = p = (char *)PyMem_Malloc(size);
270    if (buf == NULL) {
271        return PyErr_NoMemory();
272    }
273
274    if (def.rank == -1) {
275        if (def.doc) {
276            n = strlen(def.doc);
277            if (n > size) {
278                goto fail;
279            }
280            memcpy(p, def.doc, n);
281            p += n;
282            size -= n;
283        }
284        else {
285            n = PyOS_snprintf(p, size, "%s - no docs available", def.name);
286            if (n < 0 || n >= size) {
287                goto fail;
288            }
289            p += n;
290            size -= n;
291        }
292    }
293    else {
294        PyArray_Descr *d = PyArray_DescrFromType(def.type);
295        n = PyOS_snprintf(p, size, "%s : '%c'-", def.name, d->type);
296        Py_DECREF(d);
297        if (n < 0 || n >= size) {
298            goto fail;
299        }
300        p += n;
301        size -= n;
302
303        if (def.data == NULL) {
304            n = format_def(p, size, def);
305            if (n < 0) {
306                goto fail;
307            }
308            p += n;
309            size -= n;
310        }
311        else if (def.rank > 0) {
312            n = format_def(p, size, def);
313            if (n < 0) {
314                goto fail;
315            }
316            p += n;
317            size -= n;
318        }
319        else {
320            n = strlen("scalar");
321            if (size < n) {
322                goto fail;
323            }
324            memcpy(p, "scalar", n);
325            p += n;
326            size -= n;
327        }
328    }
329    if (size <= 1) {
330        goto fail;
331    }
332    *p++ = '\n';
333    size--;
334
335    /* p now points one beyond the last character of the string in buf */
336    s = PyUnicode_FromStringAndSize(buf, p - buf);
337
338    PyMem_Free(buf);
339    return s;
340
341fail:
342    fprintf(stderr,
343            "fortranobject.c: fortran_doc: len(p)=%zd>%zd=size:"
344            " too long docstring required, increase size\n",
345            p - buf, origsize);
346    PyMem_Free(buf);
347    return NULL;
348}
349
350static FortranDataDef *save_def; /* save pointer of an allocatable array */
351static void
352set_data(char *d, npy_intp *f)
353{           /* callback from Fortran */
354    if (*f) /* In fortran f=allocated(d) */
355        save_def->data = d;
356    else
357        save_def->data = NULL;
358    /* printf("set_data: d=%p,f=%d\n",d,*f); */
359}
360
361static PyObject *
362fortran_getattr(PyFortranObject *fp, char *name)
363{
364    int i, j, k, flag;
365    if (fp->dict != NULL) {
366        // python 3.13 added PyDict_GetItemRef
367#if PY_VERSION_HEX < 0x030D0000
368        PyObject *v = _PyDict_GetItemStringWithError(fp->dict, name); // noqa: borrowed-ref OK
369        if (v == NULL && PyErr_Occurred()) {
370            return NULL;
371        }
372        else if (v != NULL) {
373            Py_INCREF(v);
374            return v;
375        }
376#else
377        PyObject *v;
378        int result = PyDict_GetItemStringRef(fp->dict, name, &v);
379        if (result == -1) {
380            return NULL;
381        }
382        else if (result == 1) {
383            return v;
384        }
385#endif
386
387    }
388    for (i = 0, j = 1; i < fp->len && (j = strcmp(name, fp->defs[i].name));
389         i++)
390        ;
391    if (j == 0)
392        if (fp->defs[i].rank != -1) { /* F90 allocatable array */
393            if (fp->defs[i].func == NULL)
394                return NULL;
395            for (k = 0; k < fp->defs[i].rank; ++k) fp->defs[i].dims.d[k] = -1;
396            save_def = &fp->defs[i];
397            (*(fp->defs[i].func))(&fp->defs[i].rank, fp->defs[i].dims.d,
398                                  set_data, &flag);
399            if (flag == 2)
400                k = fp->defs[i].rank + 1;
401            else
402                k = fp->defs[i].rank;
403            if (fp->defs[i].data != NULL) { /* array is allocated */
404                PyObject *v = PyArray_New(
405                        &PyArray_Type, k, fp->defs[i].dims.d, fp->defs[i].type,
406                        NULL, fp->defs[i].data, 0, NPY_ARRAY_FARRAY, NULL);
407                if (v == NULL)
408                    return NULL;
409                /* Py_INCREF(v); */
410                return v;
411            }
412            else { /* array is not allocated */
413                Py_RETURN_NONE;
414            }
415        }
416    if (strcmp(name, "__dict__") == 0) {
417        Py_INCREF(fp->dict);
418        return fp->dict;
419    }
420    if (strcmp(name, "__doc__") == 0) {
421        PyObject *s = PyUnicode_FromString(""), *s2, *s3;
422        for (i = 0; i < fp->len; i++) {
423            s2 = fortran_doc(fp->defs[i]);
424            s3 = PyUnicode_Concat(s, s2);
425            Py_DECREF(s2);
426            Py_DECREF(s);
427            s = s3;
428        }
429        if (PyDict_SetItemString(fp->dict, name, s))
430            return NULL;
431        return s;
432    }
433    if ((strcmp(name, "_cpointer") == 0) && (fp->len == 1)) {
434        PyObject *cobj =
435                F2PyCapsule_FromVoidPtr((void *)(fp->defs[0].data), NULL);
436        if (PyDict_SetItemString(fp->dict, name, cobj))
437            return NULL;
438        return cobj;
439    }
440    PyObject *str, *ret;
441    str = PyUnicode_FromString(name);
442    ret = PyObject_GenericGetAttr((PyObject *)fp, str);
443    Py_DECREF(str);
444    return ret;
445}
446
447static int
448fortran_setattr(PyFortranObject *fp, char *name, PyObject *v)
449{
450    int i, j, flag;
451    PyArrayObject *arr = NULL;
452    for (i = 0, j = 1; i < fp->len && (j = strcmp(name, fp->defs[i].name));
453         i++)
454        ;
455    if (j == 0) {
456        if (fp->defs[i].rank == -1) {
457            PyErr_SetString(PyExc_AttributeError,
458                            "over-writing fortran routine");
459            return -1;
460        }
461        if (fp->defs[i].func != NULL) { /* is allocatable array */
462            npy_intp dims[F2PY_MAX_DIMS];
463            int k;
464            save_def = &fp->defs[i];
465            if (v != Py_None) { /* set new value (reallocate if needed --
466                                   see f2py generated code for more
467                                   details ) */
468                for (k = 0; k < fp->defs[i].rank; k++) dims[k] = -1;
469                if ((arr = array_from_pyobj(fp->defs[i].type, dims,
470                                            fp->defs[i].rank, F2PY_INTENT_IN,
471                                            v)) == NULL)
472                    return -1;
473                (*(fp->defs[i].func))(&fp->defs[i].rank, PyArray_DIMS(arr),
474                                      set_data, &flag);
475            }
476            else { /* deallocate */
477                for (k = 0; k < fp->defs[i].rank; k++) dims[k] = 0;
478                (*(fp->defs[i].func))(&fp->defs[i].rank, dims, set_data,
479                                      &flag);
480                for (k = 0; k < fp->defs[i].rank; k++) dims[k] = -1;
481            }
482            memcpy(fp->defs[i].dims.d, dims,
483                   fp->defs[i].rank * sizeof(npy_intp));
484        }
485        else { /* not allocatable array */
486            if ((arr = array_from_pyobj(fp->defs[i].type, fp->defs[i].dims.d,
487                                        fp->defs[i].rank, F2PY_INTENT_IN,
488                                        v)) == NULL)
489                return -1;
490        }
491        if (fp->defs[i].data !=
492            NULL) { /* copy Python object to Fortran array */
493            npy_intp s = PyArray_MultiplyList(fp->defs[i].dims.d,
494                                              PyArray_NDIM(arr));
495            if (s == -1)
496                s = PyArray_MultiplyList(PyArray_DIMS(arr), PyArray_NDIM(arr));
497            if (s < 0 || (memcpy(fp->defs[i].data, PyArray_DATA(arr),
498                                 s * PyArray_ITEMSIZE(arr))) == NULL) {
499                if ((PyObject *)arr != v) {
500                    Py_DECREF(arr);
501                }
502                return -1;
503            }
504            if ((PyObject *)arr != v) {
505                Py_DECREF(arr);
506            }
507        }
508        else
509            return (fp->defs[i].func == NULL ? -1 : 0);
510        return 0; /* successful */
511    }
512    if (fp->dict == NULL) {
513        fp->dict = PyDict_New();
514        if (fp->dict == NULL)
515            return -1;
516    }
517    if (v == NULL) {
518        int rv = PyDict_DelItemString(fp->dict, name);
519        if (rv < 0)
520            PyErr_SetString(PyExc_AttributeError,
521                            "delete non-existing fortran attribute");
522        return rv;
523    }
524    else
525        return PyDict_SetItemString(fp->dict, name, v);
526}
527
528static PyObject *
529fortran_call(PyFortranObject *fp, PyObject *arg, PyObject *kw)
530{
531    int i = 0;
532    /*  printf("fortran call
533        name=%s,func=%p,data=%p,%p\n",fp->defs[i].name,
534        fp->defs[i].func,fp->defs[i].data,&fp->defs[i].data); */
535    if (fp->defs[i].rank == -1) { /* is Fortran routine */
536        if (fp->defs[i].func == NULL) {
537            PyErr_Format(PyExc_RuntimeError, "no function to call");
538            return NULL;
539        }
540        else if (fp->defs[i].data == NULL)
541            /* dummy routine */
542            return (*((fortranfunc)(fp->defs[i].func)))((PyObject *)fp, arg,
543                                                        kw, NULL);
544        else
545            return (*((fortranfunc)(fp->defs[i].func)))(
546                    (PyObject *)fp, arg, kw, (void *)fp->defs[i].data);
547    }
548    PyErr_Format(PyExc_TypeError, "this fortran object is not callable");
549    return NULL;
550}
551
552static PyObject *
553fortran_repr(PyFortranObject *fp)
554{
555    PyObject *name = NULL, *repr = NULL;
556    name = PyObject_GetAttrString((PyObject *)fp, "__name__");
557    PyErr_Clear();
558    if (name != NULL && PyUnicode_Check(name)) {
559        repr = PyUnicode_FromFormat("<fortran %U>", name);
560    }
561    else {
562        repr = PyUnicode_FromString("<fortran object>");
563    }
564    Py_XDECREF(name);
565    return repr;
566}
567
568PyTypeObject PyFortran_Type = {
569        PyVarObject_HEAD_INIT(NULL, 0).tp_name = "fortran",
570        .tp_basicsize = sizeof(PyFortranObject),
571        .tp_dealloc = (destructor)fortran_dealloc,
572        .tp_getattr = (getattrfunc)fortran_getattr,
573        .tp_setattr = (setattrfunc)fortran_setattr,
574        .tp_repr = (reprfunc)fortran_repr,
575        .tp_call = (ternaryfunc)fortran_call,
576};
577
578/************************* f2py_report_atexit *******************************/
579
580#ifdef F2PY_REPORT_ATEXIT
581static int passed_time = 0;
582static int passed_counter = 0;
583static int passed_call_time = 0;
584static struct timeb start_time;
585static struct timeb stop_time;
586static struct timeb start_call_time;
587static struct timeb stop_call_time;
588static int cb_passed_time = 0;
589static int cb_passed_counter = 0;
590static int cb_passed_call_time = 0;
591static struct timeb cb_start_time;
592static struct timeb cb_stop_time;
593static struct timeb cb_start_call_time;
594static struct timeb cb_stop_call_time;
595
596extern void
597f2py_start_clock(void)
598{
599    ftime(&start_time);
600}
601extern void
602f2py_start_call_clock(void)
603{
604    f2py_stop_clock();
605    ftime(&start_call_time);
606}
607extern void
608f2py_stop_clock(void)
609{
610    ftime(&stop_time);
611    passed_time += 1000 * (stop_time.time - start_time.time);
612    passed_time += stop_time.millitm - start_time.millitm;
613}
614extern void
615f2py_stop_call_clock(void)
616{
617    ftime(&stop_call_time);
618    passed_call_time += 1000 * (stop_call_time.time - start_call_time.time);
619    passed_call_time += stop_call_time.millitm - start_call_time.millitm;
620    passed_counter += 1;
621    f2py_start_clock();
622}
623
624extern void
625f2py_cb_start_clock(void)
626{
627    ftime(&cb_start_time);
628}
629extern void
630f2py_cb_start_call_clock(void)
631{
632    f2py_cb_stop_clock();
633    ftime(&cb_start_call_time);
634}
635extern void
636f2py_cb_stop_clock(void)
637{
638    ftime(&cb_stop_time);
639    cb_passed_time += 1000 * (cb_stop_time.time - cb_start_time.time);
640    cb_passed_time += cb_stop_time.millitm - cb_start_time.millitm;
641}
642extern void
643f2py_cb_stop_call_clock(void)
644{
645    ftime(&cb_stop_call_time);
646    cb_passed_call_time +=
647            1000 * (cb_stop_call_time.time - cb_start_call_time.time);
648    cb_passed_call_time +=
649            cb_stop_call_time.millitm - cb_start_call_time.millitm;
650    cb_passed_counter += 1;
651    f2py_cb_start_clock();
652}
653
654static int f2py_report_on_exit_been_here = 0;
655extern void
656f2py_report_on_exit(int exit_flag, void *name)
657{
658    if (f2py_report_on_exit_been_here) {
659        fprintf(stderr, "             %s\n", (char *)name);
660        return;
661    }
662    f2py_report_on_exit_been_here = 1;
663    fprintf(stderr, "                      /-----------------------\\\n");
664    fprintf(stderr, "                     < F2PY performance report >\n");
665    fprintf(stderr, "                      \\-----------------------/\n");
666    fprintf(stderr, "Overall time spent in ...\n");
667    fprintf(stderr, "(a) wrapped (Fortran/C) functions           : %8d msec\n",
668            passed_call_time);
669    fprintf(stderr, "(b) f2py interface,           %6d calls  : %8d msec\n",
670            passed_counter, passed_time);
671    fprintf(stderr, "(c) call-back (Python) functions            : %8d msec\n",
672            cb_passed_call_time);
673    fprintf(stderr, "(d) f2py call-back interface, %6d calls  : %8d msec\n",
674            cb_passed_counter, cb_passed_time);
675
676    fprintf(stderr,
677            "(e) wrapped (Fortran/C) functions (actual) : %8d msec\n\n",
678            passed_call_time - cb_passed_call_time - cb_passed_time);
679    fprintf(stderr,
680            "Use -DF2PY_REPORT_ATEXIT_DISABLE to disable this message.\n");
681    fprintf(stderr, "Exit status: %d\n", exit_flag);
682    fprintf(stderr, "Modules    : %s\n", (char *)name);
683}
684#endif
685
686/********************** report on array copy ****************************/
687
688#ifdef F2PY_REPORT_ON_ARRAY_COPY
689static void
690f2py_report_on_array_copy(PyArrayObject *arr)
691{
692    const npy_intp arr_size = PyArray_Size((PyObject *)arr);
693    if (arr_size > F2PY_REPORT_ON_ARRAY_COPY) {
694        fprintf(stderr,
695                "copied an array: size=%ld, elsize=%" NPY_INTP_FMT "\n",
696                arr_size, (npy_intp)PyArray_ITEMSIZE(arr));
697    }
698}
699static void
700f2py_report_on_array_copy_fromany(void)
701{
702    fprintf(stderr, "created an array from object\n");
703}
704
705#define F2PY_REPORT_ON_ARRAY_COPY_FROMARR \
706    f2py_report_on_array_copy((PyArrayObject *)arr)
707#define F2PY_REPORT_ON_ARRAY_COPY_FROMANY f2py_report_on_array_copy_fromany()
708#else
709#define F2PY_REPORT_ON_ARRAY_COPY_FROMARR
710#define F2PY_REPORT_ON_ARRAY_COPY_FROMANY
711#endif
712
713/************************* array_from_obj *******************************/
714
715/*
716 * File: array_from_pyobj.c
717 *
718 * Description:
719 * ------------
720 * Provides array_from_pyobj function that returns a contiguous array
721 * object with the given dimensions and required storage order, either
722 * in row-major (C) or column-major (Fortran) order. The function
723 * array_from_pyobj is very flexible about its Python object argument
724 * that can be any number, list, tuple, or array.
725 *
726 * array_from_pyobj is used in f2py generated Python extension
727 * modules.
728 *
729 * Author: Pearu Peterson <pearu@cens.ioc.ee>
730 * Created: 13-16 January 2002
731 * $Id: fortranobject.c,v 1.52 2005/07/11 07:44:20 pearu Exp $
732 */
733
734static int check_and_fix_dimensions(const PyArrayObject* arr,
735                                    const int rank,
736                                    npy_intp *dims,
737                                    const char *errmess);
738
739static int
740find_first_negative_dimension(const int rank, const npy_intp *dims)
741{
742    int i;
743    for (i = 0; i < rank; ++i) {
744        if (dims[i] < 0) {
745            return i;
746        }
747    }
748    return -1;
749}
750
751#ifdef DEBUG_COPY_ND_ARRAY
752void
753dump_dims(int rank, npy_intp const *dims)
754{
755    int i;
756    printf("[");
757    for (i = 0; i < rank; ++i) {
758        printf("%3" NPY_INTP_FMT, dims[i]);
759    }
760    printf("]\n");
761}
762void
763dump_attrs(const PyArrayObject *obj)
764{
765    const PyArrayObject_fields *arr = (const PyArrayObject_fields *)obj;
766    int rank = PyArray_NDIM(arr);
767    npy_intp size = PyArray_Size((PyObject *)arr);
768    printf("\trank = %d, flags = %d, size = %" NPY_INTP_FMT "\n", rank,
769           arr->flags, size);
770    printf("\tstrides = ");
771    dump_dims(rank, arr->strides);
772    printf("\tdimensions = ");
773    dump_dims(rank, arr->dimensions);
774}
775#endif
776
777#define SWAPTYPE(a, b, t) \
778    {                     \
779        t c;              \
780        c = (a);          \
781        (a) = (b);        \
782        (b) = c;          \
783    }
784
785static int
786swap_arrays(PyArrayObject *obj1, PyArrayObject *obj2)
787{
788    PyArrayObject_fields *arr1 = (PyArrayObject_fields *)obj1,
789                         *arr2 = (PyArrayObject_fields *)obj2;
790    SWAPTYPE(arr1->data, arr2->data, char *);
791    SWAPTYPE(arr1->nd, arr2->nd, int);
792    SWAPTYPE(arr1->dimensions, arr2->dimensions, npy_intp *);
793    SWAPTYPE(arr1->strides, arr2->strides, npy_intp *);
794    SWAPTYPE(arr1->base, arr2->base, PyObject *);
795    SWAPTYPE(arr1->descr, arr2->descr, PyArray_Descr *);
796    SWAPTYPE(arr1->flags, arr2->flags, int);
797    /* SWAPTYPE(arr1->weakreflist,arr2->weakreflist,PyObject*); */
798    return 0;
799}
800
801#define ARRAY_ISCOMPATIBLE(arr,type_num)                                \
802    ((PyArray_ISINTEGER(arr) && PyTypeNum_ISINTEGER(type_num)) ||     \
803     (PyArray_ISFLOAT(arr) && PyTypeNum_ISFLOAT(type_num)) ||         \
804     (PyArray_ISCOMPLEX(arr) && PyTypeNum_ISCOMPLEX(type_num)) ||     \
805     (PyArray_ISBOOL(arr) && PyTypeNum_ISBOOL(type_num)) ||           \
806     (PyArray_ISSTRING(arr) && PyTypeNum_ISSTRING(type_num)))
807
808static int
809get_elsize(PyObject *obj) {
810  /*
811    get_elsize determines array itemsize from a Python object.  Returns
812    elsize if successful, -1 otherwise.
813
814    Supported types of the input are: numpy.ndarray, bytes, str, tuple,
815    list.
816  */
817
818  if (PyArray_Check(obj)) {
819    return PyArray_ITEMSIZE((PyArrayObject *)obj);
820  } else if (PyBytes_Check(obj)) {
821    return PyBytes_GET_SIZE(obj);
822  } else if (PyUnicode_Check(obj)) {
823    return PyUnicode_GET_LENGTH(obj);
824  } else if (PySequence_Check(obj)) {
825    PyObject* fast = PySequence_Fast(obj, "f2py:fortranobject.c:get_elsize"); // noqa: borrowed-ref OK
826    if (fast != NULL) {
827      Py_ssize_t i, n = PySequence_Fast_GET_SIZE(fast);
828      int sz, elsize = 0;
829      for (i=0; i<n; i++) {
830        sz = get_elsize(PySequence_Fast_GET_ITEM(fast, i) /* borrowed */);
831        if (sz > elsize) {
832          elsize = sz;
833        }
834      }
835      Py_DECREF(fast);
836      return elsize;
837    }
838  }
839  return -1;
840}
841
842extern PyArrayObject *
843ndarray_from_pyobj(const int type_num,
844                   const int elsize_,
845                   npy_intp *dims,
846                   const int rank,
847                   const int intent,
848                   PyObject *obj,
849                   const char *errmess) {
850    /*
851     * Return an array with given element type and shape from a Python
852     * object while taking into account the usage intent of the array.
853     *
854     * - element type is defined by type_num and elsize
855     * - shape is defined by dims and rank
856     *
857     * ndarray_from_pyobj is used to convert Python object arguments
858     * to numpy ndarrays with given type and shape that data is passed
859     * to interfaced Fortran or C functions.
860     *
861     * errmess (if not NULL), contains a prefix of an error message
862     * for an exception to be triggered within this function.
863     *
864     * Negative elsize value means that elsize is to be determined
865     * from the Python object in runtime.
866     *
867     * Note on strings
868     * ---------------
869     *
870     * String type (type_num == NPY_STRING) does not have fixed
871     * element size and, by default, the type object sets it to
872     * 0. Therefore, for string types, one has to use elsize
873     * argument. For other types, elsize value is ignored.
874     *
875     * NumPy defines the type of a fixed-width string as
876     * dtype('S<width>'). In addition, there is also dtype('c'), that
877     * appears as dtype('S1') (these have the same type_num value),
878     * but is actually different (.char attribute is either 'S' or
879     * 'c', respectively).
880     *
881     * In Fortran, character arrays and strings are different
882     * concepts.  The relation between Fortran types, NumPy dtypes,
883     * and type_num-elsize pairs, is defined as follows:
884     *
885     * character*5 foo     | dtype('S5')  | elsize=5, shape=()
886     * character(5) foo    | dtype('S1')  | elsize=1, shape=(5)
887     * character*5 foo(n)  | dtype('S5')  | elsize=5, shape=(n,)
888     * character(5) foo(n) | dtype('S1')  | elsize=1, shape=(5, n)
889     * character*(*) foo   | dtype('S')   | elsize=-1, shape=()
890     *
891     * Note about reference counting
892     * -----------------------------
893     *
894     * If the caller returns the array to Python, it must be done with
895     * Py_BuildValue("N",arr).  Otherwise, if obj!=arr then the caller
896     * must call Py_DECREF(arr).
897     *
898     * Note on intent(cache,out,..)
899     * ----------------------------
900     * Don't expect correct data when returning intent(cache) array.
901     *
902     */
903    char mess[F2PY_MESSAGE_BUFFER_SIZE];
904    PyArrayObject *arr = NULL;
905    int elsize = (elsize_ < 0 ? get_elsize(obj) : elsize_);
906    if (elsize < 0) {
907      if (errmess != NULL) {
908        strcpy(mess, errmess);
909      }
910      sprintf(mess + strlen(mess),
911              " -- failed to determine element size from %s",
912              Py_TYPE(obj)->tp_name);
913      PyErr_SetString(PyExc_SystemError, mess);
914      return NULL;
915    }
916    PyArray_Descr * descr = get_descr_from_type_and_elsize(type_num, elsize);  // new reference
917    if (descr == NULL) {
918      return NULL;
919    }
920    elsize = PyDataType_ELSIZE(descr);
921    if ((intent & F2PY_INTENT_HIDE)
922        || ((intent & F2PY_INTENT_CACHE) && (obj == Py_None))
923        || ((intent & F2PY_OPTIONAL) && (obj == Py_None))
924        ) {
925        /* intent(cache), optional, intent(hide) */
926        int ineg = find_first_negative_dimension(rank, dims);
927        if (ineg >= 0) {
928            int i;
929            strcpy(mess, "failed to create intent(cache|hide)|optional array"
930                   "-- must have defined dimensions but got (");
931            for(i = 0; i < rank; ++i)
932                sprintf(mess + strlen(mess), "%" NPY_INTP_FMT ",", dims[i]);
933            strcat(mess, ")");
934            PyErr_SetString(PyExc_ValueError, mess);
935            Py_DECREF(descr);
936            return NULL;
937        }
938        arr = (PyArrayObject *)                                      \
939          PyArray_NewFromDescr(&PyArray_Type, descr, rank, dims,
940                               NULL, NULL, !(intent & F2PY_INTENT_C), NULL);
941        if (arr == NULL) {
942          Py_DECREF(descr);
943          return NULL;
944        }
945        if (PyArray_ITEMSIZE(arr) != elsize) {
946          strcpy(mess, "failed to create intent(cache|hide)|optional array");
947          sprintf(mess+strlen(mess)," -- expected elsize=%d got %" NPY_INTP_FMT, elsize, (npy_intp)PyArray_ITEMSIZE(arr));
948          PyErr_SetString(PyExc_ValueError,mess);
949          Py_DECREF(arr);
950          return NULL;
951        }
952        if (!(intent & F2PY_INTENT_CACHE)) {
953          PyArray_FILLWBYTE(arr, 0);
954        }
955        return arr;
956    }
957
958    if (PyArray_Check(obj)) {
959        arr = (PyArrayObject *)obj;
960        if (intent & F2PY_INTENT_CACHE) {
961            /* intent(cache) */
962            if (PyArray_ISONESEGMENT(arr)
963                && PyArray_ITEMSIZE(arr) >= elsize) {
964                if (check_and_fix_dimensions(arr, rank, dims, errmess)) {
965                  Py_DECREF(descr);
966                  return NULL;
967                }
968                if (intent & F2PY_INTENT_OUT)
969                  Py_INCREF(arr);
970                Py_DECREF(descr);
971                return arr;
972            }
973            strcpy(mess, "failed to initialize intent(cache) array");
974            if (!PyArray_ISONESEGMENT(arr))
975                strcat(mess, " -- input must be in one segment");
976            if (PyArray_ITEMSIZE(arr) < elsize)
977                sprintf(mess + strlen(mess),
978                        " -- expected at least elsize=%d but got "
979                        "%" NPY_INTP_FMT,
980                        elsize, (npy_intp)PyArray_ITEMSIZE(arr));
981            PyErr_SetString(PyExc_ValueError, mess);
982            Py_DECREF(descr);
983            return NULL;
984        }
985
986        /* here we have always intent(in) or intent(inout) or intent(inplace)
987         */
988
989        if (check_and_fix_dimensions(arr, rank, dims, errmess)) {
990          Py_DECREF(descr);
991          return NULL;
992        }
993        /*
994        printf("intent alignment=%d\n", F2PY_GET_ALIGNMENT(intent));
995        printf("alignment check=%d\n", F2PY_CHECK_ALIGNMENT(arr, intent));
996        int i;
997        for (i=1;i<=16;i++)
998          printf("i=%d isaligned=%d\n", i, ARRAY_ISALIGNED(arr, i));
999        */
1000        if ((! (intent & F2PY_INTENT_COPY)) &&
1001            PyArray_ITEMSIZE(arr) == elsize &&
1002            ARRAY_ISCOMPATIBLE(arr,type_num) &&
1003            F2PY_CHECK_ALIGNMENT(arr, intent)) {
1004            if ((intent & F2PY_INTENT_INOUT || intent & F2PY_INTENT_INPLACE)
1005              ? ((intent & F2PY_INTENT_C) ? PyArray_ISCARRAY(arr) : PyArray_ISFARRAY(arr))
1006              : ((intent & F2PY_INTENT_C) ? PyArray_ISCARRAY_RO(arr) : PyArray_ISFARRAY_RO(arr))) {
1007                if ((intent & F2PY_INTENT_OUT)) {
1008                    Py_INCREF(arr);
1009                }
1010                /* Returning input array */
1011                Py_DECREF(descr);
1012                return arr;
1013            }
1014        }
1015        if (intent & F2PY_INTENT_INOUT) {
1016            strcpy(mess, "failed to initialize intent(inout) array");
1017            /* Must use PyArray_IS*ARRAY because intent(inout) requires
1018             * writable input */
1019            if ((intent & F2PY_INTENT_C) && !PyArray_ISCARRAY(arr))
1020                strcat(mess, " -- input not contiguous");
1021            if (!(intent & F2PY_INTENT_C) && !PyArray_ISFARRAY(arr))
1022                strcat(mess, " -- input not fortran contiguous");
1023            if (PyArray_ITEMSIZE(arr) != elsize)
1024                sprintf(mess + strlen(mess),
1025                        " -- expected elsize=%d but got %" NPY_INTP_FMT,
1026                        elsize,
1027                        (npy_intp)PyArray_ITEMSIZE(arr)
1028                        );
1029            if (!(ARRAY_ISCOMPATIBLE(arr, type_num))) {
1030                sprintf(mess + strlen(mess),
1031                        " -- input '%c' not compatible to '%c'",
1032                        PyArray_DESCR(arr)->type, descr->type);
1033            }
1034            if (!(F2PY_CHECK_ALIGNMENT(arr, intent)))
1035                sprintf(mess + strlen(mess), " -- input not %d-aligned",
1036                        F2PY_GET_ALIGNMENT(intent));
1037            PyErr_SetString(PyExc_ValueError, mess);
1038            Py_DECREF(descr);
1039            return NULL;
1040        }
1041
1042        /* here we have always intent(in) or intent(inplace) */
1043
1044        {
1045          PyArrayObject * retarr = (PyArrayObject *)                    \
1046            PyArray_NewFromDescr(&PyArray_Type, descr, PyArray_NDIM(arr), PyArray_DIMS(arr),
1047                                 NULL, NULL, !(intent & F2PY_INTENT_C), NULL);
1048          if (retarr==NULL) {
1049            Py_DECREF(descr);
1050            return NULL;
1051          }
1052          F2PY_REPORT_ON_ARRAY_COPY_FROMARR;
1053          if (PyArray_CopyInto(retarr, arr)) {
1054            Py_DECREF(retarr);
1055            return NULL;
1056          }
1057          if (intent & F2PY_INTENT_INPLACE) {
1058            if (swap_arrays(arr,retarr)) {
1059              Py_DECREF(retarr);
1060              return NULL; /* XXX: set exception */
1061            }
1062            Py_XDECREF(retarr);
1063            if (intent & F2PY_INTENT_OUT)
1064              Py_INCREF(arr);
1065          } else {
1066            arr = retarr;
1067          }
1068        }
1069        return arr;
1070    }
1071
1072    if ((intent & F2PY_INTENT_INOUT) || (intent & F2PY_INTENT_INPLACE) ||
1073        (intent & F2PY_INTENT_CACHE)) {
1074        PyErr_Format(PyExc_TypeError,
1075                     "failed to initialize intent(inout|inplace|cache) "
1076                     "array, input '%s' object is not an array",
1077                     Py_TYPE(obj)->tp_name);
1078        Py_DECREF(descr);
1079        return NULL;
1080    }
1081
1082    {
1083        F2PY_REPORT_ON_ARRAY_COPY_FROMANY;
1084        arr = (PyArrayObject *)PyArray_FromAny(
1085                obj, descr, 0, 0,
1086                ((intent & F2PY_INTENT_C) ? NPY_ARRAY_CARRAY
1087                                          : NPY_ARRAY_FARRAY) |
1088                        NPY_ARRAY_FORCECAST,
1089                NULL);
1090        // Warning: in the case of NPY_STRING, PyArray_FromAny may
1091        // reset descr->elsize, e.g. dtype('S0') becomes dtype('S1').
1092        if (arr == NULL) {
1093          Py_DECREF(descr);
1094          return NULL;
1095        }
1096        if (type_num != NPY_STRING && PyArray_ITEMSIZE(arr) != elsize) {
1097          // This is internal sanity tests: elsize has been set to
1098          // descr->elsize in the beginning of this function.
1099          strcpy(mess, "failed to initialize intent(in) array");
1100          sprintf(mess + strlen(mess),
1101                  " -- expected elsize=%d got %" NPY_INTP_FMT, elsize,
1102                  (npy_intp)PyArray_ITEMSIZE(arr));
1103          PyErr_SetString(PyExc_ValueError, mess);
1104          Py_DECREF(arr);
1105          return NULL;
1106        }
1107        if (check_and_fix_dimensions(arr, rank, dims, errmess)) {
1108          Py_DECREF(arr);
1109          return NULL;
1110        }
1111        return arr;
1112    }
1113}
1114
1115extern PyArrayObject *
1116array_from_pyobj(const int type_num,
1117                                npy_intp *dims,
1118                                const int rank,
1119                                const int intent,
1120                                PyObject *obj) {
1121  /*
1122    Same as ndarray_from_pyobj but with elsize determined from type,
1123    if possible. Provided for backward compatibility.
1124   */
1125  PyArray_Descr* descr = PyArray_DescrFromType(type_num);
1126  int elsize = PyDataType_ELSIZE(descr);
1127  Py_DECREF(descr);
1128  return ndarray_from_pyobj(type_num, elsize, dims, rank, intent, obj, NULL);
1129}
1130
1131/*****************************************/
1132/* Helper functions for array_from_pyobj */
1133/*****************************************/
1134
1135static int
1136check_and_fix_dimensions(const PyArrayObject* arr, const int rank,
1137                         npy_intp *dims, const char *errmess)
1138{
1139    /*
1140     * This function fills in blanks (that are -1's) in dims list using
1141     * the dimensions from arr. It also checks that non-blank dims will
1142     * match with the corresponding values in arr dimensions.
1143     *
1144     * Returns 0 if the function is successful.
1145     *
1146     * If an error condition is detected, an exception is set and 1 is
1147     * returned.
1148     */
1149    char mess[F2PY_MESSAGE_BUFFER_SIZE];
1150    const npy_intp arr_size =
1151            (PyArray_NDIM(arr)) ? PyArray_Size((PyObject *)arr) : 1;
1152#ifdef DEBUG_COPY_ND_ARRAY
1153    dump_attrs(arr);
1154    printf("check_and_fix_dimensions:init: dims=");
1155    dump_dims(rank, dims);
1156#endif
1157    if (rank > PyArray_NDIM(arr)) { /* [1,2] -> [[1],[2]]; 1 -> [[1]]  */
1158        npy_intp new_size = 1;
1159        int free_axe = -1;
1160        int i;
1161        npy_intp d;
1162        /* Fill dims where -1 or 0; check dimensions; calc new_size; */
1163        for (i = 0; i < PyArray_NDIM(arr); ++i) {
1164            d = PyArray_DIM(arr, i);
1165            if (dims[i] >= 0) {
1166                if (d > 1 && dims[i] != d) {
1167                    PyErr_Format(
1168                            PyExc_ValueError,
1169                            "%d-th dimension must be fixed to %" NPY_INTP_FMT
1170                            " but got %" NPY_INTP_FMT "\n",
1171                            i, dims[i], d);
1172                    return 1;
1173                }
1174                if (!dims[i])
1175                    dims[i] = 1;
1176            }
1177            else {
1178                dims[i] = d ? d : 1;
1179            }
1180            new_size *= dims[i];
1181        }
1182        for (i = PyArray_NDIM(arr); i < rank; ++i)
1183            if (dims[i] > 1) {
1184                PyErr_Format(PyExc_ValueError,
1185                             "%d-th dimension must be %" NPY_INTP_FMT
1186                             " but got 0 (not defined).\n",
1187                             i, dims[i]);
1188                return 1;
1189            }
1190            else if (free_axe < 0)
1191                free_axe = i;
1192            else
1193                dims[i] = 1;
1194        if (free_axe >= 0) {
1195            dims[free_axe] = arr_size / new_size;
1196            new_size *= dims[free_axe];
1197        }
1198        if (new_size != arr_size) {
1199            PyErr_Format(PyExc_ValueError,
1200                         "unexpected array size: new_size=%" NPY_INTP_FMT

Showing the first 1,200 of 1437 lines. Download the file for the rest.

codekingpro/portable-devtools · Team Ai