From 87d30413e64887b98482a764c55806a63d682ab7 Mon Sep 17 00:00:00 2001 From: Sebastian Berg Date: Tue, 28 Apr 2026 13:28:59 +0200 Subject: [PATCH] MAINT: Simplify sorting tags (I didn't try to remove them...) Manaas sorting PR added a huge amount of duplication, but while this all might have made sense with C++11, the whole pattern is a ridiculous amount of code-duplication with C++17. This cleans it up, I used claude with guidance/follow-up, not sure if the `_type` pattern is best, but it works well and only changes the `_tag` header for now, which is nice. For the next step this should make things rather simple: We can introduce a `cmp` template function (at worst per kind), I think. (It also removed the unused `_SWAP` macros, the most complicated change is moving the half helpers into the `numpy_tag.h` file, which is nicer, IMO.) --- numpy/_core/src/common/numpy_tag.h | 431 +++++++++++------------ numpy/_core/src/npysort/npysort_common.h | 372 +------------------ 2 files changed, 209 insertions(+), 594 deletions(-) diff --git a/numpy/_core/src/common/numpy_tag.h b/numpy/_core/src/common/numpy_tag.h index ee0c36cacd73..8ff4b1069ed5 100644 --- a/numpy/_core/src/common/numpy_tag.h +++ b/numpy/_core/src/common/numpy_tag.h @@ -1,259 +1,224 @@ -#ifndef _NPY_COMMON_TAG_H_ -#define _NPY_COMMON_TAG_H_ +#ifndef NUMPY_CORE_SRC_COMMON_NUMPY_TAG_H_ +#define NUMPY_CORE_SRC_COMMON_NUMPY_TAG_H_ -#include "../npysort/npysort_common.h" +#include "numpy/ndarraytypes.h" +#include "numpy/npy_common.h" +#include "numpy/npy_math.h" + +#include +#include + +/* + * Per-dtype tags shared by the sort/partition/binsearch and clip + * implementations. + * + * Each tag exposes: + * + * - ``type`` -- the underlying C scalar type + * - ``type_value`` -- the corresponding ``NPY_TYPES`` enumerator + * - ``less`` / ``less_equal`` -- the sort-friendly comparisons that + * propagate NaN / NaT to the high end + * + * For the four numeric categories that need different NaN/NaT handling, + * comparisons are implemented once at the ``*_type`` + * template level (with ``if constexpr`` for the only per-scalar variation + * -- the three real/imag accessors used by the complex types). Each + * such template inherits from a tiny empty marker (``integral_tag``, + * ``floating_point_tag``, ``complex_tag``, ``date_tag``) so that + * ``clip.cpp`` can dispatch via ordinary overload resolution. + * + * Concrete tags (``bool_tag``, ``float_tag``, ...) are then plain aliases + * for an ``*_type`` instantiation. + * + * Distinct tag types matter because several of NumPy's scalar types alias + * one another at the C level -- most notably ``npy_half`` is a typedef for + * ``npy_uint16`` -- so the extra ``NPY_TYPES`` template argument is what + * keeps ``half_tag`` and ``ushort_tag`` apart. + */ namespace npy { -template -struct taglist { - static constexpr unsigned size = sizeof...(tags); -}; +// Category markers used by clip's overload-set dispatch. ``half_tag`` is +// its own marker (and the only half tag) so it does not need one here. +struct integral_tag {}; +struct floating_point_tag {}; +struct complex_tag {}; +struct date_tag {}; -struct integral_tag { -}; -struct floating_point_tag { -}; -struct complex_tag { -}; -struct date_tag { +template +struct integral_type : integral_tag { + using type = T; + static constexpr NPY_TYPES type_value = TypeNum; + static int less(T a, T b) { return a < b; } + static int less_equal(T a, T b) { return !(b < a); } }; -struct bool_tag : integral_tag { - using type = npy_bool; - static constexpr NPY_TYPES type_value = NPY_BOOL; - static int less(type const& a, type const& b) { - return BOOL_LT(a, b); - } - static int less_equal(type const& a, type const& b) { - return !less(b, a); - } -}; -struct byte_tag : integral_tag { - using type = npy_byte; - static constexpr NPY_TYPES type_value = NPY_BYTE; - static int less(type const& a, type const& b) { - return BYTE_LT(a, b); - } - static int less_equal(type const& a, type const& b) { - return !less(b, a); - } -}; -struct ubyte_tag : integral_tag { - using type = npy_ubyte; - static constexpr NPY_TYPES type_value = NPY_UBYTE; - static int less(type const& a, type const& b) { - return UBYTE_LT(a, b); - } - static int less_equal(type const& a, type const& b) { - return !less(b, a); - } -}; -struct short_tag : integral_tag { - using type = npy_short; - static constexpr NPY_TYPES type_value = NPY_SHORT; - static int less(type const& a, type const& b) { - return SHORT_LT(a, b); - } - static int less_equal(type const& a, type const& b) { - return !less(b, a); - } -}; -struct ushort_tag : integral_tag { - using type = npy_ushort; - static constexpr NPY_TYPES type_value = NPY_USHORT; - static int less(type const& a, type const& b) { - return USHORT_LT(a, b); - } - static int less_equal(type const& a, type const& b) { - return !less(b, a); - } -}; -struct int_tag : integral_tag { - using type = npy_int; - static constexpr NPY_TYPES type_value = NPY_INT; - static int less(type const& a, type const& b) { - return INT_LT(a, b); - } - static int less_equal(type const& a, type const& b) { - return !less(b, a); - } -}; -struct uint_tag : integral_tag { - using type = npy_uint; - static constexpr NPY_TYPES type_value = NPY_UINT; - static int less(type const& a, type const& b) { - return UINT_LT(a, b); - } - static int less_equal(type const& a, type const& b) { - return !less(b, a); - } -}; -struct long_tag : integral_tag { - using type = npy_long; - static constexpr NPY_TYPES type_value = NPY_LONG; - static int less(type const& a, type const& b) { - return LONG_LT(a, b); - } - static int less_equal(type const& a, type const& b) { - return !less(b, a); - } -}; -struct ulong_tag : integral_tag { - using type = npy_ulong; - static constexpr NPY_TYPES type_value = NPY_ULONG; - static int less(type const& a, type const& b) { - return ULONG_LT(a, b); - } - static int less_equal(type const& a, type const& b) { - return !less(b, a); - } -}; -struct longlong_tag : integral_tag { - using type = npy_longlong; - static constexpr NPY_TYPES type_value = NPY_LONGLONG; - static int less(type const& a, type const& b) { - return LONGLONG_LT(a, b); - } - static int less_equal(type const& a, type const& b) { - return !less(b, a); - } -}; -struct ulonglong_tag : integral_tag { - using type = npy_ulonglong; - static constexpr NPY_TYPES type_value = NPY_ULONGLONG; - static int less(type const& a, type const& b) { - return ULONGLONG_LT(a, b); - } - static int less_equal(type const& a, type const& b) { - return !less(b, a); - } +template +struct floating_point_type : floating_point_tag { + using type = T; + static constexpr NPY_TYPES type_value = TypeNum; + // NaN sorts to the end: a is "less than" b if a is non-NaN and + // either a < b or b is NaN. ``x != x`` is the IEEE NaN test. + static int less(T a, T b) { return a < b || (b != b && a == a); } + static int less_equal(T a, T b) { return !less(b, a); } }; + +// Half is its own per-type tag; no template since there is only one half +// scalar. It also serves directly as its category marker for clip. struct half_tag { using type = npy_half; static constexpr NPY_TYPES type_value = NPY_HALF; - static int less(type const& a, type const& b) { - return HALF_LT(a, b); - } - static int less_equal(type const& a, type const& b) { - return !less(b, a); - } -}; -struct float_tag : floating_point_tag { - using type = npy_float; - static constexpr NPY_TYPES type_value = NPY_FLOAT; - static int less(type const& a, type const& b) { - return FLOAT_LT(a, b); - } - static int less_equal(type const& a, type const& b) { - return !less(b, a); - } -}; -struct double_tag : floating_point_tag { - using type = npy_double; - static constexpr NPY_TYPES type_value = NPY_DOUBLE; - static int less(type const& a, type const& b) { - return DOUBLE_LT(a, b); - } - static int less_equal(type const& a, type const& b) { - return !less(b, a); - } -}; -struct longdouble_tag : floating_point_tag { - using type = npy_longdouble; - static constexpr NPY_TYPES type_value = NPY_LONGDOUBLE; - static int less(type const& a, type const& b) { - return LONGDOUBLE_LT(a, b); - } - static int less_equal(type const& a, type const& b) { - return !less(b, a); - } -}; -struct cfloat_tag : complex_tag { - using type = npy_cfloat; - static constexpr NPY_TYPES type_value = NPY_CFLOAT; - static int less(type const& a, type const& b) { - return CFLOAT_LT(a, b); - } - static int less_equal(type const& a, type const& b) { - return !less(b, a); - } -}; -struct cdouble_tag : complex_tag { - using type = npy_cdouble; - static constexpr NPY_TYPES type_value = NPY_CDOUBLE; - static int less(type const& a, type const& b) { - return CDOUBLE_LT(a, b); - } - static int less_equal(type const& a, type const& b) { - return !less(b, a); + + static int isnan(npy_half h) + { + return ((h & 0x7c00u) == 0x7c00u) && ((h & 0x03ffu) != 0x0000u); } -}; -struct clongdouble_tag : complex_tag { - using type = npy_clongdouble; - static constexpr NPY_TYPES type_value = NPY_CLONGDOUBLE; - static int less(type const& a, type const& b) { - return CLONGDOUBLE_LT(a, b); + + // Bit-level less-than that assumes neither operand is NaN. + static int lt_nonan(npy_half a, npy_half b) + { + if (a & 0x8000u) { + if (b & 0x8000u) { + return (a & 0x7fffu) > (b & 0x7fffu); + } + // Signed zeros compare equal. + return (a != 0x8000u) || (b != 0x0000u); + } + if (b & 0x8000u) { + return 0; + } + return (a & 0x7fffu) < (b & 0x7fffu); } - static int less_equal(type const& a, type const& b) { - return !less(b, a); + + static int less(npy_half a, npy_half b) + { + if (isnan(b)) { + return !isnan(a); + } + return !isnan(a) && lt_nonan(a, b); } + static int less_equal(npy_half a, npy_half b) { return !less(b, a); } }; -struct datetime_tag : date_tag { - using type = npy_datetime; - static constexpr NPY_TYPES type_value = NPY_DATETIME; - static int less(type const& a, type const& b) { - return DATETIME_LT(a, b); - } - static int less_equal(type const& a, type const& b) { - return !less(b, a); + +template +struct complex_type : complex_tag { + using type = T; + static constexpr NPY_TYPES type_value = TypeNum; + + // Real / imag accessors picked at compile time so ``less`` can be + // written generically across the three complex scalar types. + static auto creal(T z) + { + if constexpr (std::is_same_v) return npy_crealf(z); + else if constexpr (std::is_same_v) return npy_creal(z); + else return npy_creall(z); + } + static auto cimag(T z) + { + if constexpr (std::is_same_v) return npy_cimagf(z); + else if constexpr (std::is_same_v) return npy_cimag(z); + else return npy_cimagl(z); } + + static int less(T a, T b) + { + const auto ra = creal(a), rb = creal(b); + const auto ia = cimag(a), ib = cimag(b); + if (ra < rb) { + return ia == ia || ib != ib; + } + if (ra > rb) { + return ib != ib && ia == ia; + } + if (ra == rb || (ra != ra && rb != rb)) { + return ia < ib || (ib != ib && ia == ia); + } + return rb != rb; + } + static int less_equal(T a, T b) { return !less(b, a); } }; -struct timedelta_tag : date_tag { - using type = npy_timedelta; - static constexpr NPY_TYPES type_value = NPY_TIMEDELTA; - static int less(type const& a, type const& b) { - return TIMEDELTA_LT(a, b); - } - static int less_equal(type const& a, type const& b) { - return !less(b, a); - } + +template +struct datetime_type : date_tag { + using type = T; + static constexpr NPY_TYPES type_value = TypeNum; + static int less(T a, T b) + { + if (a == NPY_DATETIME_NAT) return 0; + if (b == NPY_DATETIME_NAT) return 1; + return a < b; + } + static int less_equal(T a, T b) { return !less(b, a); } }; -struct string_tag { - using type = npy_char; - static constexpr NPY_TYPES type_value = NPY_STRING; - static int less(type const* a, type const* b, size_t len) { - return STRING_LT(a, b, len); - } - static int less_equal(type const* a, type const* b, size_t len) { - return !less(b, a, len); - } - static void swap(type* a, type* b, size_t len) { - STRING_SWAP(a, b, len); - } - static void copy(type * a, type const* b, size_t len) { - STRING_COPY(a, b, len); +// String / unicode tags work on runtime-length blocks. Comparison is +// unsigned (matches strcmp/wcscmp ordering) regardless of whether ``T`` is +// a signed ``char`` on the platform. +template +struct string_like_type { + using type = T; + static constexpr NPY_TYPES type_value = TypeNum; + + static int less(T const *a, T const *b, size_t n) + { + using U = std::make_unsigned_t; + const auto *ua = reinterpret_cast(a); + const auto *ub = reinterpret_cast(b); + for (size_t i = 0; i < n; ++i) { + if (ua[i] != ub[i]) { + return ua[i] < ub[i]; + } + } + return 0; + } + static int less_equal(T const *a, T const *b, size_t n) + { + return !less(b, a, n); + } + static void swap(T *a, T *b, size_t n) + { + for (size_t i = 0; i < n; ++i) { + T t = a[i]; + a[i] = b[i]; + b[i] = t; + } + } + static void copy(T *a, T const *b, size_t n) + { + std::memcpy(a, b, n * sizeof(T)); } }; -struct unicode_tag { - using type = npy_ucs4; - static constexpr NPY_TYPES type_value = NPY_UNICODE; - static int less(type const* a, type const* b, size_t len) { - return UNICODE_LT(a, b, len); - } - static int less_equal(type const* a, type const* b, size_t len) { - return !less(b, a, len); - } - static void swap(type* a, type* b, size_t len) { - UNICODE_SWAP(a, b, len); - } - static void copy(type * a, type const* b, size_t len) { - UNICODE_COPY(a, b, len); - } +// Concrete tags consumed by callers. +using bool_tag = integral_type; +using byte_tag = integral_type; +using ubyte_tag = integral_type; +using short_tag = integral_type; +using ushort_tag = integral_type; +using int_tag = integral_type; +using uint_tag = integral_type; +using long_tag = integral_type; +using ulong_tag = integral_type; +using longlong_tag = integral_type; +using ulonglong_tag = integral_type; +using float_tag = floating_point_type; +using double_tag = floating_point_type; +using longdouble_tag = floating_point_type; +using cfloat_tag = complex_type; +using cdouble_tag = complex_type; +using clongdouble_tag = complex_type; +using datetime_tag = datetime_type; +using timedelta_tag = datetime_type; +using string_tag = string_like_type; +using unicode_tag = string_like_type; + +// Type-list helper used by selection.cpp / binsearch.cpp to instantiate one +// function per supported tag. +template +struct taglist { + static constexpr unsigned size = sizeof...(Tags); }; } // namespace npy -#endif +#endif // NUMPY_CORE_SRC_COMMON_NUMPY_TAG_H_ diff --git a/numpy/_core/src/npysort/npysort_common.h b/numpy/_core/src/npysort/npysort_common.h index f2b99e3b7f66..f49ca4af8799 100644 --- a/numpy/_core/src/npysort/npysort_common.h +++ b/numpy/_core/src/npysort/npysort_common.h @@ -1,9 +1,7 @@ -#ifndef __NPY_SORT_COMMON_H__ -#define __NPY_SORT_COMMON_H__ +#ifndef NUMPY_CORE_SRC_NPYSORT_NPYSORT_COMMON_H_ +#define NUMPY_CORE_SRC_NPYSORT_NPYSORT_COMMON_H_ #include -#include -#include #include "dtypemeta.h" #ifdef __cplusplus @@ -11,40 +9,14 @@ extern "C" { #endif /* - ***************************************************************************** - ** SWAP MACROS ** - ***************************************************************************** + * Shared helpers used by the per-dtype sort implementations. Per-dtype + * less-than comparisons live on the tags in ``numpy_tag.h``; this header + * only carries the small handful of helpers used by the generic (cmp- + * function-driven) sort code and by the argsort variants. */ -#define BOOL_SWAP(a,b) {npy_bool tmp = (b); (b)=(a); (a) = tmp;} -#define BYTE_SWAP(a,b) {npy_byte tmp = (b); (b)=(a); (a) = tmp;} -#define UBYTE_SWAP(a,b) {npy_ubyte tmp = (b); (b)=(a); (a) = tmp;} -#define SHORT_SWAP(a,b) {npy_short tmp = (b); (b)=(a); (a) = tmp;} -#define USHORT_SWAP(a,b) {npy_ushort tmp = (b); (b)=(a); (a) = tmp;} -#define INT_SWAP(a,b) {npy_int tmp = (b); (b)=(a); (a) = tmp;} -#define UINT_SWAP(a,b) {npy_uint tmp = (b); (b)=(a); (a) = tmp;} -#define LONG_SWAP(a,b) {npy_long tmp = (b); (b)=(a); (a) = tmp;} -#define ULONG_SWAP(a,b) {npy_ulong tmp = (b); (b)=(a); (a) = tmp;} -#define LONGLONG_SWAP(a,b) {npy_longlong tmp = (b); (b)=(a); (a) = tmp;} -#define ULONGLONG_SWAP(a,b) {npy_ulonglong tmp = (b); (b)=(a); (a) = tmp;} -#define HALF_SWAP(a,b) {npy_half tmp = (b); (b)=(a); (a) = tmp;} -#define FLOAT_SWAP(a,b) {npy_float tmp = (b); (b)=(a); (a) = tmp;} -#define DOUBLE_SWAP(a,b) {npy_double tmp = (b); (b)=(a); (a) = tmp;} -#define LONGDOUBLE_SWAP(a,b) {npy_longdouble tmp = (b); (b)=(a); (a) = tmp;} -#define CFLOAT_SWAP(a,b) {npy_cfloat tmp = (b); (b)=(a); (a) = tmp;} -#define CDOUBLE_SWAP(a,b) {npy_cdouble tmp = (b); (b)=(a); (a) = tmp;} -#define CLONGDOUBLE_SWAP(a,b) {npy_clongdouble tmp = (b); (b)=(a); (a) = tmp;} -#define DATETIME_SWAP(a,b) {npy_datetime tmp = (b); (b)=(a); (a) = tmp;} -#define TIMEDELTA_SWAP(a,b) {npy_timedelta tmp = (b); (b)=(a); (a) = tmp;} - -/* Need this for the argsort functions */ -#define INTP_SWAP(a,b) {npy_intp tmp = (b); (b)=(a); (a) = tmp;} - -/* - ****************************************************************************** - ** SORTING WRAPPERS ** - ****************************************************************************** - */ +/* Argsort works on indices, swap macro for npy_intp. */ +#define INTP_SWAP(a, b) {npy_intp tmp = (b); (b) = (a); (a) = tmp;} static inline void get_sort_data_from_array(void *varr, npy_intp *elsize, PyArray_CompareFunc **cmp) @@ -54,339 +26,17 @@ get_sort_data_from_array(void *varr, npy_intp *elsize, PyArray_CompareFunc **cmp *cmp = PyDataType_GetArrFuncs(PyArray_DESCR(arr))->compare; } -/* - ***************************************************************************** - ** COMPARISON FUNCTIONS ** - ***************************************************************************** - */ - -static inline int -BOOL_LT(npy_bool a, npy_bool b) -{ - return a < b; -} - - -static inline int -BYTE_LT(npy_byte a, npy_byte b) -{ - return a < b; -} - - -static inline int -UBYTE_LT(npy_ubyte a, npy_ubyte b) -{ - return a < b; -} - - -static inline int -SHORT_LT(npy_short a, npy_short b) -{ - return a < b; -} - - -static inline int -USHORT_LT(npy_ushort a, npy_ushort b) -{ - return a < b; -} - - -static inline int -INT_LT(npy_int a, npy_int b) -{ - return a < b; -} - - -static inline int -UINT_LT(npy_uint a, npy_uint b) -{ - return a < b; -} - - -static inline int -LONG_LT(npy_long a, npy_long b) -{ - return a < b; -} - - -static inline int -ULONG_LT(npy_ulong a, npy_ulong b) -{ - return a < b; -} - - -static inline int -LONGLONG_LT(npy_longlong a, npy_longlong b) -{ - return a < b; -} - - -static inline int -ULONGLONG_LT(npy_ulonglong a, npy_ulonglong b) -{ - return a < b; -} - - -static inline int -FLOAT_LT(npy_float a, npy_float b) -{ - return a < b || (b != b && a == a); -} - - -static inline int -DOUBLE_LT(npy_double a, npy_double b) -{ - return a < b || (b != b && a == a); -} - - -static inline int -LONGDOUBLE_LT(npy_longdouble a, npy_longdouble b) -{ - return a < b || (b != b && a == a); -} - - -static inline int -_npy_half_isnan(npy_half h) -{ - return ((h&0x7c00u) == 0x7c00u) && ((h&0x03ffu) != 0x0000u); -} - - -static inline int -_npy_half_lt_nonan(npy_half h1, npy_half h2) -{ - if (h1&0x8000u) { - if (h2&0x8000u) { - return (h1&0x7fffu) > (h2&0x7fffu); - } - else { - /* Signed zeros are equal, have to check for it */ - return (h1 != 0x8000u) || (h2 != 0x0000u); - } - } - else { - if (h2&0x8000u) { - return 0; - } - else { - return (h1&0x7fffu) < (h2&0x7fffu); - } - } -} - - -static inline int -HALF_LT(npy_half a, npy_half b) -{ - int ret; - - if (_npy_half_isnan(b)) { - ret = !_npy_half_isnan(a); - } - else { - ret = !_npy_half_isnan(a) && _npy_half_lt_nonan(a, b); - } - - return ret; -} - -/* - * For inline functions SUN recommends not using a return in the then part - * of an if statement. It's a SUN compiler thing, so assign the return value - * to a variable instead. - */ -static inline int -CFLOAT_LT(npy_cfloat a, npy_cfloat b) -{ - int ret; - - if (npy_crealf(a) < npy_crealf(b)) { - ret = npy_cimagf(a) == npy_cimagf(a) || npy_cimagf(b) != npy_cimagf(b); - } - else if (npy_crealf(a) > npy_crealf(b)) { - ret = npy_cimagf(b) != npy_cimagf(b) && npy_cimagf(a) == npy_cimagf(a); - } - else if (npy_crealf(a) == npy_crealf(b) || (npy_crealf(a) != npy_crealf(a) && npy_crealf(b) != npy_crealf(b))) { - ret = npy_cimagf(a) < npy_cimagf(b) || (npy_cimagf(b) != npy_cimagf(b) && npy_cimagf(a) == npy_cimagf(a)); - } - else { - ret = npy_crealf(b) != npy_crealf(b); - } - - return ret; -} - - -static inline int -CDOUBLE_LT(npy_cdouble a, npy_cdouble b) -{ - int ret; - - if (npy_creal(a) < npy_creal(b)) { - ret = npy_cimag(a) == npy_cimag(a) || npy_cimag(b) != npy_cimag(b); - } - else if (npy_creal(a) > npy_creal(b)) { - ret = npy_cimag(b) != npy_cimag(b) && npy_cimag(a) == npy_cimag(a); - } - else if (npy_creal(a) == npy_creal(b) || (npy_creal(a) != npy_creal(a) && npy_creal(b) != npy_creal(b))) { - ret = npy_cimag(a) < npy_cimag(b) || (npy_cimag(b) != npy_cimag(b) && npy_cimag(a) == npy_cimag(a)); - } - else { - ret = npy_creal(b) != npy_creal(b); - } - - return ret; -} - - -static inline int -CLONGDOUBLE_LT(npy_clongdouble a, npy_clongdouble b) -{ - int ret; - - if (npy_creall(a) < npy_creall(b)) { - ret = npy_cimagl(a) == npy_cimagl(a) || npy_cimagl(b) != npy_cimagl(b); - } - else if (npy_creall(a) > npy_creall(b)) { - ret = npy_cimagl(b) != npy_cimagl(b) && npy_cimagl(a) == npy_cimagl(a); - } - else if (npy_creall(a) == npy_creall(b) || (npy_creall(a) != npy_creall(a) && npy_creall(b) != npy_creall(b))) { - ret = npy_cimagl(a) < npy_cimagl(b) || (npy_cimagl(b) != npy_cimagl(b) && npy_cimagl(a) == npy_cimagl(a)); - } - else { - ret = npy_creall(b) != npy_creall(b); - } - - return ret; -} - - -static inline void -STRING_COPY(char *s1, char const*s2, size_t len) -{ - memcpy(s1, s2, len); -} - - -static inline void -STRING_SWAP(char *s1, char *s2, size_t len) -{ - while(len--) { - const char t = *s1; - *s1++ = *s2; - *s2++ = t; - } -} - - -static inline int -STRING_LT(const char *s1, const char *s2, size_t len) -{ - const unsigned char *c1 = (const unsigned char *)s1; - const unsigned char *c2 = (const unsigned char *)s2; - size_t i; - int ret = 0; - - for (i = 0; i < len; ++i) { - if (c1[i] != c2[i]) { - ret = c1[i] < c2[i]; - break; - } - } - return ret; -} - - -static inline void -UNICODE_COPY(npy_ucs4 *s1, npy_ucs4 const *s2, size_t len) -{ - while(len--) { - *s1++ = *s2++; - } -} - - -static inline void -UNICODE_SWAP(npy_ucs4 *s1, npy_ucs4 *s2, size_t len) -{ - while(len--) { - const npy_ucs4 t = *s1; - *s1++ = *s2; - *s2++ = t; - } -} - - -static inline int -UNICODE_LT(const npy_ucs4 *s1, const npy_ucs4 *s2, size_t len) -{ - size_t i; - int ret = 0; - - for (i = 0; i < len; ++i) { - if (s1[i] != s2[i]) { - ret = s1[i] < s2[i]; - break; - } - } - return ret; -} - - -static inline int -DATETIME_LT(npy_datetime a, npy_datetime b) -{ - if (a == NPY_DATETIME_NAT) { - return 0; - } - - if (b == NPY_DATETIME_NAT) { - return 1; - } - - return a < b; -} - - -static inline int -TIMEDELTA_LT(npy_timedelta a, npy_timedelta b) -{ - if (a == NPY_DATETIME_NAT) { - return 0; - } - - if (b == NPY_DATETIME_NAT) { - return 1; - } - - return a < b; -} - - +/* Element copy / swap for the generic, comparison-function-driven sort. */ static inline void GENERIC_COPY(char *a, char *b, size_t len) { memcpy(a, b, len); } - static inline void GENERIC_SWAP(char *a, char *b, size_t len) { - while(len--) { + while (len--) { const char t = *a; *a++ = *b; *b++ = t; @@ -397,4 +47,4 @@ GENERIC_SWAP(char *a, char *b, size_t len) } #endif -#endif +#endif /* NUMPY_CORE_SRC_NPYSORT_NPYSORT_COMMON_H_ */