numpy 2.0.0
src/private/lowlevel_strided_loops.h
Go to the documentation of this file.
00001 #ifndef __LOWLEVEL_STRIDED_LOOPS_H
00002 #define __LOWLEVEL_STRIDED_LOOPS_H
00003 
00004 /*
00005  * NOTE: This API should remain private for the time being, to allow
00006  *       for further refinement.  I think the 'aligned' mechanism
00007  *       needs changing, for example.
00008  */
00009 
00010 /*
00011  * This function pointer is for functions that transfer an arbitrarily strided
00012  * input to a an arbitrarily strided output.  It may be a fully general
00013  * function, or a specialized function when the strides or item size
00014  * have special values.
00015  *
00016  * Examples of transfer functions are a straight copy, a byte-swap,
00017  * and a casting operation,
00018  *
00019  * The 'transferdata' parameter is slightly special, and must always contain
00020  * pointer to deallocation and copying routines at its beginning.  The function
00021  * PyArray_FreeStridedTransferData should be used to deallocate such
00022  * pointers, and calls the first function pointer, while the function
00023  * PyArray_CopyStridedTransferData should be used to copy it.
00024  * 
00025  */
00026 typedef void (PyArray_StridedTransferFn)(char *dst, npy_intp dst_stride,
00027                                     char *src, npy_intp src_stride,
00028                                     npy_intp N, npy_intp src_itemsize,
00029                                     void *transferdata);
00030 
00031 /*
00032  * Deallocates a PyArray_StridedTransferFunction data object.  See
00033  * the comment with the function typedef for more details.
00034  */
00035 NPY_NO_EXPORT void
00036 PyArray_FreeStridedTransferData(void *transferdata);
00037 
00038 /*
00039  * Copies a PyArray_StridedTransferFunction data object.  See
00040  * the comment with the function typedef for more details.
00041  */
00042 NPY_NO_EXPORT void *
00043 PyArray_CopyStridedTransferData(void *transferdata);
00044 
00045 /*
00046  * Gives back a function pointer to a specialized function for copying
00047  * strided memory.  Returns NULL if there is a problem with the inputs.
00048  *
00049  * aligned:
00050  *      Should be 1 if the src and dst pointers are always aligned,
00051  *      0 otherwise.
00052  * src_stride:  
00053  *      Should be the src stride if it will always be the same,
00054  *      NPY_MAX_INTP otherwise.
00055  * dst_stride:  
00056  *      Should be the dst stride if it will always be the same,
00057  *      NPY_MAX_INTP otherwise.
00058  * itemsize:
00059  *      Should be the item size if it will always be the same, 0 otherwise.
00060  *
00061  */
00062 NPY_NO_EXPORT PyArray_StridedTransferFn *
00063 PyArray_GetStridedCopyFn(npy_intp aligned, npy_intp src_stride,
00064                          npy_intp dst_stride, npy_intp itemsize);
00065 
00066 /*
00067  * Gives back a function pointer to a specialized function for copying
00068  * and swapping strided memory.  This assumes each element is a single
00069  * value to be swapped.
00070  *
00071  * For information on the 'aligned', 'src_stride' and 'dst_stride' parameters
00072  * see above.
00073  *
00074  * Parameters are as for PyArray_GetStridedCopyFn.
00075  */
00076 NPY_NO_EXPORT PyArray_StridedTransferFn *
00077 PyArray_GetStridedCopySwapFn(npy_intp aligned, npy_intp src_stride,
00078                              npy_intp dst_stride, npy_intp itemsize);
00079 
00080 /*
00081  * Gives back a function pointer to a specialized function for copying
00082  * and swapping strided memory.  This assumes each element is a pair
00083  * of values, each of which needs to be swapped.
00084  *
00085  * For information on the 'aligned', 'src_stride' and 'dst_stride' parameters
00086  * see above.
00087  *
00088  * Parameters are as for PyArray_GetStridedCopyFn.
00089  */
00090 NPY_NO_EXPORT PyArray_StridedTransferFn *
00091 PyArray_GetStridedCopySwapPairFn(npy_intp aligned, npy_intp src_stride,
00092                              npy_intp dst_stride, npy_intp itemsize);
00093 
00094 /*
00095  * Gives back a transfer function and transfer data pair which copies
00096  * the data from source to dest, truncating it if the data doesn't
00097  * fit, and padding with zero bytes if there's too much space.
00098  *
00099  * For information on the 'aligned', 'src_stride' and 'dst_stride' parameters
00100  * see above.
00101  *
00102  * Returns NPY_SUCCEED or NPY_FAIL
00103  */
00104 NPY_NO_EXPORT int
00105 PyArray_GetStridedZeroPadCopyFn(int aligned,
00106                             npy_intp src_stride, npy_intp dst_stride,
00107                             npy_intp src_itemsize, npy_intp dst_itemsize,
00108                             PyArray_StridedTransferFn **outstransfer,
00109                             void **outtransferdata);
00110 
00111 /*
00112  * For casts between built-in numeric types,
00113  * this produces a function pointer for casting from src_type_num
00114  * to dst_type_num.  If a conversion is unsupported, returns NULL
00115  * without setting a Python exception.
00116  */
00117 NPY_NO_EXPORT PyArray_StridedTransferFn *
00118 PyArray_GetStridedNumericCastFn(npy_intp aligned, npy_intp src_stride,
00119                              npy_intp dst_stride,
00120                              int src_type_num, int dst_type_num);
00121 
00122 /*
00123  * If it's possible, gives back a transfer function which casts and/or
00124  * byte swaps data with the dtype 'src_dtype' into data with the dtype
00125  * 'dst_dtype'.  If the outtransferdata is populated with a non-NULL value,
00126  * it must be deallocated with the ``PyArray_FreeStridedTransferData``
00127  * function when the transfer function is no longer required.
00128  *
00129  * aligned:
00130  *      Should be 1 if the src and dst pointers are always aligned,
00131  *      0 otherwise.
00132  * src_stride:  
00133  *      Should be the src stride if it will always be the same,
00134  *      NPY_MAX_INTP otherwise.
00135  * dst_stride:  
00136  *      Should be the dst stride if it will always be the same,
00137  *      NPY_MAX_INTP otherwise.
00138  * src_dtype:
00139  *      The data type of source data.  If this is NULL, a transfer
00140  *      function which sets the destination to zeros is produced.
00141  * dst_dtype:
00142  *      The data type of destination data.  If this is NULL and
00143  *      move_references is 1, a transfer function which decrements
00144  *      source data references is produced.
00145  * move_references:
00146  *      If 0, the destination data gets new reference ownership.
00147  *      If 1, the references from the source data are moved to
00148  *      the destination data.
00149  * out_stransfer:
00150  *      The resulting transfer function is placed here.
00151  * out_transferdata:
00152  *      The auxiliary data for the transfer function is placed here.
00153  *      When finished with the transfer function, the caller must call
00154  *      ``PyArray_FreeStridedTransferData`` on this data.
00155  * out_needs_api:
00156  *      If this is non-NULL, and the transfer function produced needs
00157  *      to call into the (Python) API, this gets set to 1.  This
00158  *      remains untouched if no API access is required.
00159  *
00160  * WARNING: If you set move_references to 1, it is best that src_stride is
00161  *          never zero when calling the transfer function.  Otherwise, the
00162  *          first destination reference will get the value and all the rest
00163  *          will get NULL.
00164  *
00165  * Returns NPY_SUCCEED or NPY_FAIL.
00166  */
00167 NPY_NO_EXPORT int
00168 PyArray_GetDTypeTransferFunction(int aligned,
00169                             npy_intp src_stride, npy_intp dst_stride,
00170                             PyArray_Descr *src_dtype, PyArray_Descr *dst_dtype,
00171                             int move_references,
00172                             PyArray_StridedTransferFn **out_stransfer,
00173                             void **out_transferdata,
00174                             int *out_needs_api);
00175 
00176 /*
00177  * These two functions copy or convert the data of an n-dimensional array
00178  * to/from a 1-dimensional strided buffer.  These functions will only call
00179  * 'stransfer' with the provided dst_stride/src_stride and
00180  * dst_strides[0]/src_strides[0], so the caller can use those values to
00181  * specialize the function.
00182  *
00183  * The return value is the number of elements it couldn't copy.  A return value
00184  * of 0 means all elements were copied, a larger value means the end of
00185  * the n-dimensional array was reached before 'count' elements were copied.
00186  *
00187  * ndim:
00188  *      The number of dimensions of the n-dimensional array.
00189  * dst/src:
00190  *      The destination or src starting pointer.
00191  * dst_stride/src_stride:
00192  *      The stride of the 1-dimensional strided buffer
00193  * dst_strides/src_strides:
00194  *      The strides of the n-dimensional array.
00195  * dst_strides_inc/src_strides_inc:
00196  *      How much to add to the ..._strides pointer to get to the next stride.
00197  * coords:
00198  *      The starting coordinates in the n-dimensional array.
00199  * coords_inc:
00200  *      How much to add to the coords pointer to get to the next coordinate.
00201  * shape:
00202  *      The shape of the n-dimensional array.
00203  * shape_inc:
00204  *      How much to add to the shape pointer to get to the next shape entry.
00205  * count:
00206  *      How many elements to transfer
00207  * src_itemsize:
00208  *      How big each element is.  If transfering between elements of different
00209  *      sizes, for example a casting operation, the 'stransfer' function
00210  *      should be specialized for that, in which case 'stransfer' will use
00211  *      this parameter as the source item size.
00212  * stransfer:
00213  *      The strided transfer function.
00214  * transferdata:
00215  *      An auxiliary data pointer passed to the strided transfer function.
00216  *      If a non-NULL value is returned, it must be deallocated with the
00217  *      function PyArray_FreeStridedTransferData.
00218  */
00219 NPY_NO_EXPORT npy_intp
00220 PyArray_TransferNDimToStrided(npy_intp ndim,
00221                 char *dst, npy_intp dst_stride,
00222                 char *src, npy_intp *src_strides, npy_intp src_strides_inc,
00223                 npy_intp *coords, npy_intp coords_inc,
00224                 npy_intp *shape, npy_intp shape_inc,
00225                 npy_intp count, npy_intp src_itemsize,
00226                 PyArray_StridedTransferFn *stransfer,
00227                 void *transferdata);
00228 
00229 NPY_NO_EXPORT npy_intp
00230 PyArray_TransferStridedToNDim(npy_intp ndim,
00231                 char *dst, npy_intp *dst_strides, npy_intp dst_strides_inc,
00232                 char *src, npy_intp src_stride,
00233                 npy_intp *coords, npy_intp coords_inc,
00234                 npy_intp *shape, npy_intp shape_inc,
00235                 npy_intp count, npy_intp src_itemsize,
00236                 PyArray_StridedTransferFn *stransfer,
00237                 void *transferdata);
00238 
00239 /*
00240  *            TRIVIAL ITERATION
00241  *
00242  * In some cases when the iteration order isn't important, iteration over
00243  * arrays is trivial.  This is the case when:
00244  *   * The array has 0 or 1 dimensions.
00245  *   * The array is C or Fortran contiguous.
00246  * Use of an iterator can be skipped when this occurs.  These macros assist
00247  * in detecting and taking advantage of the situation.  Note that it may
00248  * be worthwhile to further check if the stride is a contiguous stride
00249  * and take advantage of that.
00250  *
00251  * Here is example code for a single array:
00252  *
00253  *      if (PyArray_TRIVIALLY_ITERABLE(self) {
00254  *          char *data;
00255  *          npy_intp count, stride;
00256  *
00257  *          PyArray_PREPARE_TRIVIAL_ITERATION(self, count, data, stride);
00258  *
00259  *          while (count--) {
00260  *              // Use the data pointer
00261  *
00262  *              data += stride;
00263  *          }
00264  *      }
00265  *      else {
00266  *          // Create iterator, etc...
00267  *      }
00268  *
00269  * Here is example code for a pair of arrays:
00270  *
00271  *      if (PyArray_TRIVIALLY_ITERABLE_PAIR(a1, a2) {
00272  *          char *data1, *data2;
00273  *          npy_intp count, stride1, stride2;
00274  *
00275  *          PyArray_PREPARE_TRIVIAL_PAIR_ITERATION(a1, a2, count,
00276  *                                  data1, data2, stride1, stride2);
00277  *
00278  *          while (count--) {
00279  *              // Use the data1 and data2 pointers
00280  *
00281  *              data1 += stride1;
00282  *              data2 += stride2;
00283  *          }
00284  *      }
00285  *      else {
00286  *          // Create iterator, etc...
00287  *      }
00288  */
00289 
00290 /*
00291  * Note: Equivalently iterable macro requires one of arr1 or arr2 be
00292  *       trivially iterable to be valid.
00293  */
00294 #define PyArray_EQUIVALENTLY_ITERABLE(arr1, arr2) ( \
00295                         PyArray_NDIM(arr1) == PyArray_NDIM(arr2) && \
00296                         PyArray_CompareLists(PyArray_DIMS(arr1), \
00297                                              PyArray_DIMS(arr2), \
00298                                              PyArray_NDIM(arr1)) && \
00299                         (arr1->flags&(NPY_CONTIGUOUS|NPY_FORTRAN)) == \
00300                                 (arr2->flags&(NPY_CONTIGUOUS|NPY_FORTRAN)) \
00301                         )
00302 
00303 #define PyArray_TRIVIALLY_ITERABLE(arr) ( \
00304                     PyArray_NDIM(arr) <= 1 || \
00305                     PyArray_CHKFLAGS(arr, NPY_CONTIGUOUS) || \
00306                     PyArray_CHKFLAGS(arr, NPY_FORTRAN) \
00307                     )
00308 #define PyArray_PREPARE_TRIVIAL_ITERATION(arr, count, data, stride) \
00309                     count = PyArray_SIZE(arr), \
00310                     data = PyArray_BYTES(arr), \
00311                     stride = ((PyArray_NDIM(arr) == 0) ? 0 : \
00312                                 (PyArray_CHKFLAGS(arr, NPY_FORTRAN) ? \
00313                                             PyArray_STRIDE(arr, 0) : \
00314                                             PyArray_STRIDE(arr, \
00315                                                 PyArray_NDIM(arr)-1)))
00316 
00317 #define PyArray_TRIVIALLY_ITERABLE_PAIR(arr1, arr2) (\
00318                     PyArray_TRIVIALLY_ITERABLE(arr1) && \
00319                         (PyArray_NDIM(arr2) == 0 || \
00320                          PyArray_EQUIVALENTLY_ITERABLE(arr1, arr2) || \
00321                          (PyArray_NDIM(arr1) == 0 && \
00322                              PyArray_TRIVIALLY_ITERABLE(arr2) \
00323                          ) \
00324                         ) \
00325                     )
00326 #define PyArray_PREPARE_TRIVIAL_PAIR_ITERATION(arr1, arr2, \
00327                                         count, \
00328                                         data1, data2, \
00329                                         stride1, stride2) { \
00330                     npy_intp size1 = PyArray_SIZE(arr1); \
00331                     npy_intp size2 = PyArray_SIZE(arr2); \
00332                     count = ((size1 > size2) || size1 == 0) ? size1 : size2; \
00333                     data1 = PyArray_BYTES(arr1); \
00334                     data2 = PyArray_BYTES(arr2); \
00335                     stride1 = (size1 == 1 ? 0 : \
00336                                 (PyArray_CHKFLAGS(arr1, NPY_FORTRAN) ? \
00337                                             PyArray_STRIDE(arr1, 0) : \
00338                                             PyArray_STRIDE(arr1, \
00339                                                 PyArray_NDIM(arr1)-1))); \
00340                     stride2 = (size2 == 1 ? 0 : \
00341                                 (PyArray_CHKFLAGS(arr2, NPY_FORTRAN) ? \
00342                                             PyArray_STRIDE(arr2, 0) : \
00343                                             PyArray_STRIDE(arr2, \
00344                                                 PyArray_NDIM(arr2)-1))); \
00345                 }
00346 
00347 #define PyArray_TRIVIALLY_ITERABLE_TRIPLE(arr1, arr2, arr3) (\
00348                 PyArray_TRIVIALLY_ITERABLE(arr1) && \
00349                     ((PyArray_NDIM(arr2) == 0 && \
00350                         (PyArray_NDIM(arr3) == 0 || \
00351                             PyArray_EQUIVALENTLY_ITERABLE(arr1, arr3) \
00352                         ) \
00353                      ) || \
00354                      (PyArray_EQUIVALENTLY_ITERABLE(arr1, arr2) && \
00355                         (PyArray_NDIM(arr3) == 0 || \
00356                             PyArray_EQUIVALENTLY_ITERABLE(arr1, arr3) \
00357                         ) \
00358                      ) || \
00359                      (PyArray_NDIM(arr1) == 0 && \
00360                         PyArray_TRIVIALLY_ITERABLE(arr2) && \
00361                             (PyArray_NDIM(arr3) == 0 || \
00362                                 PyArray_EQUIVALENTLY_ITERABLE(arr2, arr3) \
00363                             ) \
00364                      ) \
00365                     ) \
00366                 )
00367 
00368 #define PyArray_PREPARE_TRIVIAL_TRIPLE_ITERATION(arr1, arr2, arr3, \
00369                                         count, \
00370                                         data1, data2, data3, \
00371                                         stride1, stride2, stride3) { \
00372                     npy_intp size1 = PyArray_SIZE(arr1); \
00373                     npy_intp size2 = PyArray_SIZE(arr2); \
00374                     npy_intp size3 = PyArray_SIZE(arr3); \
00375                     count = ((size1 > size2) || size1 == 0) ? size1 : size2; \
00376                     count = ((size3 > count) || size3 == 0) ? size3 : count; \
00377                     data1 = PyArray_BYTES(arr1); \
00378                     data2 = PyArray_BYTES(arr2); \
00379                     data3 = PyArray_BYTES(arr3); \
00380                     stride1 = (size1 == 1 ? 0 : \
00381                                 (PyArray_CHKFLAGS(arr1, NPY_FORTRAN) ? \
00382                                             PyArray_STRIDE(arr1, 0) : \
00383                                             PyArray_STRIDE(arr1, \
00384                                                 PyArray_NDIM(arr1)-1))); \
00385                     stride2 = (size2 == 1 ? 0 : \
00386                                 (PyArray_CHKFLAGS(arr2, NPY_FORTRAN) ? \
00387                                             PyArray_STRIDE(arr2, 0) : \
00388                                             PyArray_STRIDE(arr2, \
00389                                                 PyArray_NDIM(arr2)-1))); \
00390                     stride3 = (size3 == 1 ? 0 : \
00391                                 (PyArray_CHKFLAGS(arr3, NPY_FORTRAN) ? \
00392                                             PyArray_STRIDE(arr3, 0) : \
00393                                             PyArray_STRIDE(arr3, \
00394                                                 PyArray_NDIM(arr3)-1))); \
00395                 }
00396 
00397 #endif