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, following a
00020  * generic auxiliary data pattern defined in ndarraytypes.h
00021  * Use NPY_AUXDATA_CLONE and NPY_AUXDATA_FREE to deal with this data.
00022  * 
00023  */
00024 typedef void (PyArray_StridedTransferFn)(char *dst, npy_intp dst_stride,
00025                                     char *src, npy_intp src_stride,
00026                                     npy_intp N, npy_intp src_itemsize,
00027                                     NpyAuxData *transferdata);
00028 
00029 /*
00030  * This is for pointers to functions which behave exactly as
00031  * for PyArray_StridedTransferFn, but with an additional mask controlling
00032  * which values are transferred.
00033  *
00034  * In particular, the 'i'-th element is transfered if and only if
00035  * NpyMask_IsExposed(mask[i*mask_stride]).
00036  */
00037 typedef void (PyArray_MaskedStridedTransferFn)(char *dst, npy_intp dst_stride,
00038                                     char *src, npy_intp src_stride,
00039                                     npy_mask *mask, npy_intp mask_stride,
00040                                     npy_intp N, npy_intp src_itemsize,
00041                                     NpyAuxData *transferdata);
00042 
00043 /*
00044  * Gives back a function pointer to a specialized function for copying
00045  * strided memory.  Returns NULL if there is a problem with the inputs.
00046  *
00047  * aligned:
00048  *      Should be 1 if the src and dst pointers are always aligned,
00049  *      0 otherwise.
00050  * src_stride:  
00051  *      Should be the src stride if it will always be the same,
00052  *      NPY_MAX_INTP otherwise.
00053  * dst_stride:  
00054  *      Should be the dst stride if it will always be the same,
00055  *      NPY_MAX_INTP otherwise.
00056  * itemsize:
00057  *      Should be the item size if it will always be the same, 0 otherwise.
00058  *
00059  */
00060 NPY_NO_EXPORT PyArray_StridedTransferFn *
00061 PyArray_GetStridedCopyFn(int aligned,
00062                         npy_intp src_stride, npy_intp dst_stride,
00063                         npy_intp itemsize);
00064 
00065 /*
00066  * Gives back a function pointer to a specialized function for copying
00067  * and swapping strided memory.  This assumes each element is a single
00068  * value to be swapped.
00069  *
00070  * For information on the 'aligned', 'src_stride' and 'dst_stride' parameters
00071  * see above.
00072  *
00073  * Parameters are as for PyArray_GetStridedCopyFn.
00074  */
00075 NPY_NO_EXPORT PyArray_StridedTransferFn *
00076 PyArray_GetStridedCopySwapFn(int aligned,
00077                             npy_intp src_stride, npy_intp dst_stride,
00078                             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(int aligned,
00092                             npy_intp src_stride, npy_intp dst_stride,
00093                             npy_intp itemsize);
00094 
00095 /*
00096  * Gives back a transfer function and transfer data pair which copies
00097  * the data from source to dest, truncating it if the data doesn't
00098  * fit, and padding with zero bytes if there's too much space.
00099  *
00100  * For information on the 'aligned', 'src_stride' and 'dst_stride' parameters
00101  * see above.
00102  *
00103  * Returns NPY_SUCCEED or NPY_FAIL
00104  */
00105 NPY_NO_EXPORT int
00106 PyArray_GetStridedZeroPadCopyFn(int aligned,
00107                             npy_intp src_stride, npy_intp dst_stride,
00108                             npy_intp src_itemsize, npy_intp dst_itemsize,
00109                             PyArray_StridedTransferFn **outstransfer,
00110                             NpyAuxData **outtransferdata);
00111 
00112 /*
00113  * For casts between built-in numeric types,
00114  * this produces a function pointer for casting from src_type_num
00115  * to dst_type_num.  If a conversion is unsupported, returns NULL
00116  * without setting a Python exception.
00117  */
00118 NPY_NO_EXPORT PyArray_StridedTransferFn *
00119 PyArray_GetStridedNumericCastFn(int aligned,
00120                             npy_intp src_stride, npy_intp dst_stride,
00121                             int src_type_num, int dst_type_num);
00122 
00123 /*
00124  * Gets an operation which copies elements of the given dtype,
00125  * swapping if the dtype isn't in NBO.
00126  *
00127  * Returns NPY_SUCCEED or NPY_FAIL
00128  */
00129 NPY_NO_EXPORT int
00130 PyArray_GetDTypeCopySwapFn(int aligned,
00131                             npy_intp src_stride, npy_intp dst_stride,
00132                             PyArray_Descr *dtype,
00133                             PyArray_StridedTransferFn **outstransfer,
00134                             NpyAuxData **outtransferdata);
00135 
00136 /*
00137  * If it's possible, gives back a transfer function which casts and/or
00138  * byte swaps data with the dtype 'src_dtype' into data with the dtype
00139  * 'dst_dtype'.  If the outtransferdata is populated with a non-NULL value,
00140  * it must be deallocated with the ``PyArray_FreeStridedTransferData``
00141  * function when the transfer function is no longer required.
00142  *
00143  * aligned:
00144  *      Should be 1 if the src and dst pointers are always aligned,
00145  *      0 otherwise.
00146  * src_stride:  
00147  *      Should be the src stride if it will always be the same,
00148  *      NPY_MAX_INTP otherwise.
00149  * dst_stride:  
00150  *      Should be the dst stride if it will always be the same,
00151  *      NPY_MAX_INTP otherwise.
00152  * src_dtype:
00153  *      The data type of source data.  If this is NULL, a transfer
00154  *      function which sets the destination to zeros is produced.
00155  * dst_dtype:
00156  *      The data type of destination data.  If this is NULL and
00157  *      move_references is 1, a transfer function which decrements
00158  *      source data references is produced.
00159  * move_references:
00160  *      If 0, the destination data gets new reference ownership.
00161  *      If 1, the references from the source data are moved to
00162  *      the destination data.
00163  * out_stransfer:
00164  *      The resulting transfer function is placed here.
00165  * out_transferdata:
00166  *      The auxiliary data for the transfer function is placed here.
00167  *      When finished with the transfer function, the caller must call
00168  *      ``PyArray_FreeStridedTransferData`` on this data.
00169  * out_needs_api:
00170  *      If this is non-NULL, and the transfer function produced needs
00171  *      to call into the (Python) API, this gets set to 1.  This
00172  *      remains untouched if no API access is required.
00173  *
00174  * WARNING: If you set move_references to 1, it is best that src_stride is
00175  *          never zero when calling the transfer function.  Otherwise, the
00176  *          first destination reference will get the value and all the rest
00177  *          will get NULL.
00178  *
00179  * Returns NPY_SUCCEED or NPY_FAIL.
00180  */
00181 NPY_NO_EXPORT int
00182 PyArray_GetDTypeTransferFunction(int aligned,
00183                             npy_intp src_stride, npy_intp dst_stride,
00184                             PyArray_Descr *src_dtype, PyArray_Descr *dst_dtype,
00185                             int move_references,
00186                             PyArray_StridedTransferFn **out_stransfer,
00187                             NpyAuxData **out_transferdata,
00188                             int *out_needs_api);
00189 
00190 /*
00191  * This is identical to PyArray_GetDTypeTransferFunction, but
00192  * returns a transfer function which also takes a mask as a parameter.
00193  * Bit zero of the mask is used to determine which values to copy,
00194  * and data is transfered exactly when NpyMask_IsExposed(mask[i*mask_stride]).
00195  *
00196  * If move_references is true, values which are not copied to the
00197  * destination will still have their source reference decremented.
00198  *
00199  * If mask_dtype is NPY_BOOL or NPY_UINT8, each full element is either
00200  * transferred or not according to the mask as described above. If
00201  * dst_dtype and mask_dtype are both struct dtypes, their names must
00202  * match exactly, and the dtype of each leaf field in mask_dtype must
00203  * be either NPY_BOOL or NPY_UINT8.
00204  */
00205 NPY_NO_EXPORT int
00206 PyArray_GetMaskedDTypeTransferFunction(int aligned,
00207                             npy_intp src_stride,
00208                             npy_intp dst_stride,
00209                             npy_intp mask_stride,
00210                             PyArray_Descr *src_dtype,
00211                             PyArray_Descr *dst_dtype,
00212                             PyArray_Descr *mask_dtype,
00213                             int move_references,
00214                             PyArray_MaskedStridedTransferFn **out_stransfer,
00215                             NpyAuxData **out_transferdata,
00216                             int *out_needs_api);
00217 
00218 /*
00219  * Casts the specified number of elements from 'src' with data type
00220  * 'src_dtype' to 'dst' with 'dst_dtype'. See
00221  * PyArray_GetDTypeTransferFunction for more details.
00222  *
00223  * returns NPY_SUCCEED or NPY_FAIL.
00224  */
00225 NPY_NO_EXPORT int
00226 PyArray_CastRawArrays(npy_intp count,
00227                       char *src, char *dst,
00228                       npy_intp src_stride, npy_intp dst_stride,
00229                       PyArray_Descr *src_dtype, PyArray_Descr *dst_dtype,
00230                       int move_references);
00231 
00232 /*
00233  * These two functions copy or convert the data of an n-dimensional array
00234  * to/from a 1-dimensional strided buffer.  These functions will only call
00235  * 'stransfer' with the provided dst_stride/src_stride and
00236  * dst_strides[0]/src_strides[0], so the caller can use those values to
00237  * specialize the function.
00238  *
00239  * The return value is the number of elements it couldn't copy.  A return value
00240  * of 0 means all elements were copied, a larger value means the end of
00241  * the n-dimensional array was reached before 'count' elements were copied.
00242  *
00243  * ndim:
00244  *      The number of dimensions of the n-dimensional array.
00245  * dst/src/mask:
00246  *      The destination, source or mask starting pointer.
00247  * dst_stride/src_stride/mask_stride:
00248  *      The stride of the 1-dimensional strided buffer
00249  * dst_strides/src_strides:
00250  *      The strides of the n-dimensional array.
00251  * dst_strides_inc/src_strides_inc:
00252  *      How much to add to the ..._strides pointer to get to the next stride.
00253  * coords:
00254  *      The starting coordinates in the n-dimensional array.
00255  * coords_inc:
00256  *      How much to add to the coords pointer to get to the next coordinate.
00257  * shape:
00258  *      The shape of the n-dimensional array.
00259  * shape_inc:
00260  *      How much to add to the shape pointer to get to the next shape entry.
00261  * count:
00262  *      How many elements to transfer
00263  * src_itemsize:
00264  *      How big each element is.  If transfering between elements of different
00265  *      sizes, for example a casting operation, the 'stransfer' function
00266  *      should be specialized for that, in which case 'stransfer' will use
00267  *      this parameter as the source item size.
00268  * stransfer:
00269  *      The strided transfer function.
00270  * transferdata:
00271  *      An auxiliary data pointer passed to the strided transfer function.
00272  *      This follows the conventions of NpyAuxData objects.
00273  */
00274 NPY_NO_EXPORT npy_intp
00275 PyArray_TransferNDimToStrided(npy_intp ndim,
00276                 char *dst, npy_intp dst_stride,
00277                 char *src, npy_intp *src_strides, npy_intp src_strides_inc,
00278                 npy_intp *coords, npy_intp coords_inc,
00279                 npy_intp *shape, npy_intp shape_inc,
00280                 npy_intp count, npy_intp src_itemsize,
00281                 PyArray_StridedTransferFn *stransfer,
00282                 NpyAuxData *transferdata);
00283 
00284 NPY_NO_EXPORT npy_intp
00285 PyArray_TransferStridedToNDim(npy_intp ndim,
00286                 char *dst, npy_intp *dst_strides, npy_intp dst_strides_inc,
00287                 char *src, npy_intp src_stride,
00288                 npy_intp *coords, npy_intp coords_inc,
00289                 npy_intp *shape, npy_intp shape_inc,
00290                 npy_intp count, npy_intp src_itemsize,
00291                 PyArray_StridedTransferFn *stransfer,
00292                 NpyAuxData *transferdata);
00293 
00294 NPY_NO_EXPORT npy_intp
00295 PyArray_TransferMaskedStridedToNDim(npy_intp ndim,
00296                 char *dst, npy_intp *dst_strides, npy_intp dst_strides_inc,
00297                 char *src, npy_intp src_stride,
00298                 npy_mask *mask, npy_intp mask_stride,
00299                 npy_intp *coords, npy_intp coords_inc,
00300                 npy_intp *shape, npy_intp shape_inc,
00301                 npy_intp count, npy_intp src_itemsize,
00302                 PyArray_MaskedStridedTransferFn *stransfer,
00303                 NpyAuxData *data);
00304 
00305 
00306 /*
00307  *            TRIVIAL ITERATION
00308  *
00309  * In some cases when the iteration order isn't important, iteration over
00310  * arrays is trivial.  This is the case when:
00311  *   * The array has 0 or 1 dimensions.
00312  *   * The array is C or Fortran contiguous.
00313  * Use of an iterator can be skipped when this occurs.  These macros assist
00314  * in detecting and taking advantage of the situation.  Note that it may
00315  * be worthwhile to further check if the stride is a contiguous stride
00316  * and take advantage of that.
00317  *
00318  * Here is example code for a single array:
00319  *
00320  *      if (PyArray_TRIVIALLY_ITERABLE(self) {
00321  *          char *data;
00322  *          npy_intp count, stride;
00323  *
00324  *          PyArray_PREPARE_TRIVIAL_ITERATION(self, count, data, stride);
00325  *
00326  *          while (count--) {
00327  *              // Use the data pointer
00328  *
00329  *              data += stride;
00330  *          }
00331  *      }
00332  *      else {
00333  *          // Create iterator, etc...
00334  *      }
00335  *
00336  * Here is example code for a pair of arrays:
00337  *
00338  *      if (PyArray_TRIVIALLY_ITERABLE_PAIR(a1, a2) {
00339  *          char *data1, *data2;
00340  *          npy_intp count, stride1, stride2;
00341  *
00342  *          PyArray_PREPARE_TRIVIAL_PAIR_ITERATION(a1, a2, count,
00343  *                                  data1, data2, stride1, stride2);
00344  *
00345  *          while (count--) {
00346  *              // Use the data1 and data2 pointers
00347  *
00348  *              data1 += stride1;
00349  *              data2 += stride2;
00350  *          }
00351  *      }
00352  *      else {
00353  *          // Create iterator, etc...
00354  *      }
00355  */
00356 
00357 /*
00358  * Note: Equivalently iterable macro requires one of arr1 or arr2 be
00359  *       trivially iterable to be valid.
00360  */
00361 #define PyArray_EQUIVALENTLY_ITERABLE(arr1, arr2) ( \
00362                         PyArray_NDIM(arr1) == PyArray_NDIM(arr2) && \
00363                         PyArray_CompareLists(PyArray_DIMS(arr1), \
00364                                              PyArray_DIMS(arr2), \
00365                                              PyArray_NDIM(arr1)) && \
00366                         (arr1->flags&(NPY_ARRAY_C_CONTIGUOUS| \
00367                                       NPY_ARRAY_F_CONTIGUOUS)) == \
00368                                 (arr2->flags&(NPY_ARRAY_C_CONTIGUOUS| \
00369                                               NPY_ARRAY_F_CONTIGUOUS)) \
00370                         )
00371 
00372 #define PyArray_TRIVIALLY_ITERABLE(arr) ( \
00373                     PyArray_NDIM(arr) <= 1 || \
00374                     PyArray_CHKFLAGS(arr, NPY_ARRAY_C_CONTIGUOUS) || \
00375                     PyArray_CHKFLAGS(arr, NPY_ARRAY_F_CONTIGUOUS) \
00376                     )
00377 #define PyArray_PREPARE_TRIVIAL_ITERATION(arr, count, data, stride) \
00378                     count = PyArray_SIZE(arr), \
00379                     data = PyArray_BYTES(arr), \
00380                     stride = ((PyArray_NDIM(arr) == 0) ? 0 : \
00381                             (PyArray_CHKFLAGS(arr, NPY_ARRAY_F_CONTIGUOUS) ? \
00382                                             PyArray_STRIDE(arr, 0) : \
00383                                             PyArray_STRIDE(arr, \
00384                                                 PyArray_NDIM(arr)-1)))
00385 
00386 #define PyArray_TRIVIALLY_ITERABLE_PAIR(arr1, arr2) (\
00387                     PyArray_TRIVIALLY_ITERABLE(arr1) && \
00388                         (PyArray_NDIM(arr2) == 0 || \
00389                          PyArray_EQUIVALENTLY_ITERABLE(arr1, arr2) || \
00390                          (PyArray_NDIM(arr1) == 0 && \
00391                              PyArray_TRIVIALLY_ITERABLE(arr2) \
00392                          ) \
00393                         ) \
00394                     )
00395 #define PyArray_PREPARE_TRIVIAL_PAIR_ITERATION(arr1, arr2, \
00396                                         count, \
00397                                         data1, data2, \
00398                                         stride1, stride2) { \
00399                     npy_intp size1 = PyArray_SIZE(arr1); \
00400                     npy_intp size2 = PyArray_SIZE(arr2); \
00401                     count = ((size1 > size2) || size1 == 0) ? size1 : size2; \
00402                     data1 = PyArray_BYTES(arr1); \
00403                     data2 = PyArray_BYTES(arr2); \
00404                     stride1 = (size1 == 1 ? 0 : \
00405                             (PyArray_CHKFLAGS(arr1, NPY_ARRAY_F_CONTIGUOUS) ? \
00406                                             PyArray_STRIDE(arr1, 0) : \
00407                                             PyArray_STRIDE(arr1, \
00408                                                 PyArray_NDIM(arr1)-1))); \
00409                     stride2 = (size2 == 1 ? 0 : \
00410                             (PyArray_CHKFLAGS(arr2, NPY_ARRAY_F_CONTIGUOUS) ? \
00411                                             PyArray_STRIDE(arr2, 0) : \
00412                                             PyArray_STRIDE(arr2, \
00413                                                 PyArray_NDIM(arr2)-1))); \
00414                 }
00415 
00416 #define PyArray_TRIVIALLY_ITERABLE_TRIPLE(arr1, arr2, arr3) (\
00417                 PyArray_TRIVIALLY_ITERABLE(arr1) && \
00418                     ((PyArray_NDIM(arr2) == 0 && \
00419                         (PyArray_NDIM(arr3) == 0 || \
00420                             PyArray_EQUIVALENTLY_ITERABLE(arr1, arr3) \
00421                         ) \
00422                      ) || \
00423                      (PyArray_EQUIVALENTLY_ITERABLE(arr1, arr2) && \
00424                         (PyArray_NDIM(arr3) == 0 || \
00425                             PyArray_EQUIVALENTLY_ITERABLE(arr1, arr3) \
00426                         ) \
00427                      ) || \
00428                      (PyArray_NDIM(arr1) == 0 && \
00429                         PyArray_TRIVIALLY_ITERABLE(arr2) && \
00430                             (PyArray_NDIM(arr3) == 0 || \
00431                                 PyArray_EQUIVALENTLY_ITERABLE(arr2, arr3) \
00432                             ) \
00433                      ) \
00434                     ) \
00435                 )
00436 
00437 #define PyArray_PREPARE_TRIVIAL_TRIPLE_ITERATION(arr1, arr2, arr3, \
00438                                         count, \
00439                                         data1, data2, data3, \
00440                                         stride1, stride2, stride3) { \
00441                     npy_intp size1 = PyArray_SIZE(arr1); \
00442                     npy_intp size2 = PyArray_SIZE(arr2); \
00443                     npy_intp size3 = PyArray_SIZE(arr3); \
00444                     count = ((size1 > size2) || size1 == 0) ? size1 : size2; \
00445                     count = ((size3 > count) || size3 == 0) ? size3 : count; \
00446                     data1 = PyArray_BYTES(arr1); \
00447                     data2 = PyArray_BYTES(arr2); \
00448                     data3 = PyArray_BYTES(arr3); \
00449                     stride1 = (size1 == 1 ? 0 : \
00450                             (PyArray_CHKFLAGS(arr1, NPY_ARRAY_F_CONTIGUOUS) ? \
00451                                             PyArray_STRIDE(arr1, 0) : \
00452                                             PyArray_STRIDE(arr1, \
00453                                                 PyArray_NDIM(arr1)-1))); \
00454                     stride2 = (size2 == 1 ? 0 : \
00455                             (PyArray_CHKFLAGS(arr2, NPY_ARRAY_F_CONTIGUOUS) ? \
00456                                             PyArray_STRIDE(arr2, 0) : \
00457                                             PyArray_STRIDE(arr2, \
00458                                                 PyArray_NDIM(arr2)-1))); \
00459                     stride3 = (size3 == 1 ? 0 : \
00460                             (PyArray_CHKFLAGS(arr3, NPY_ARRAY_F_CONTIGUOUS) ? \
00461                                             PyArray_STRIDE(arr3, 0) : \
00462                                             PyArray_STRIDE(arr3, \
00463                                                 PyArray_NDIM(arr3)-1))); \
00464                 }
00465 
00466 #endif