NumPy 源码解析(七十六)
.\numpy\numpy\_core\src\umath\reduction.h
/************************************************************
* Typedefs used by PyArray_ReduceWrapper, new in 1.7.
************************************************************/
/*
* This typedef defines a function pointer type for assigning a reduction identity
* to the result array before performing the reduction computation. The `data`
* parameter is passed through from PyArray_ReduceWrapper.
*
* This function should return -1 on failure or 0 on success.
*/
typedef int (PyArray_AssignReduceIdentityFunc)(PyArrayObject *result,
void *data);
/*
* This typedef defines a function pointer type for the inner definition of the reduce loop.
* It was intended for customization of the reduce loop at a lower level per ufunc.
* This aspect of the API might be deprecated or restructured in future versions.
*/
typedef int (PyArray_ReduceLoopFunc)(PyArrayMethod_Context *context,
PyArrayMethod_StridedLoop *strided_loop,
NpyAuxData *auxdata,
NpyIter *iter,
char **dataptrs,
npy_intp const *strides,
npy_intp const *countptr,
NpyIter_IterNextFunc *iternext,
int needs_api,
npy_intp skip_first_count);
/*
* This function executes all the standard NumPy reduction function
* boilerplate code, just calling the appropriate inner loop function where
* necessary.
*
* operand : The array to be reduced.
* out : NULL, or the array into which to place the result.
* wheremask : NOT YET SUPPORTED, but this parameter is placed here
* so that support can be added in the future without breaking
* API compatibility. Pass in NULL.
* operand_dtype : The dtype the inner loop expects for the operand.
* result_dtype : The dtype the inner loop expects for the result.
* casting : The casting rule to apply to the operands.
* axis_flags : Flags indicating the reduction axes of 'operand'.
* reorderable : If True, the reduction being done is reorderable, which
* means specifying multiple axes of reduction at once is ok,
* and the reduction code may calculate the reduction in an
* arbitrary order. The calculation may be reordered because
* of cache behavior or multithreading requirements.
* keepdims : If true, leaves the reduction dimensions in the result
* with size one.
* identity : If Py_None, PyArray_CopyInitialReduceValues is used, otherwise
* this value is used to initialize the result to
* the reduction's unit.
* loop : The loop which does the reduction.
* data : Data which is passed to the inner loop.
* buffersize : Buffer size for the iterator. For the default, pass in 0.
* funcname : The name of the reduction function, for error messages.
* errormask : forwarded from _get_bufsize_errmask
*/
NPY_NO_EXPORT PyArrayObject *
PyUFunc_ReduceWrapper(PyArrayMethod_Context *context,
PyArrayObject *operand, PyArrayObject *out, PyArrayObject *wheremask,
npy_bool *axis_flags, int keepdims,
PyObject *initial, PyArray_ReduceLoopFunc *loop,
npy_intp buffersize, const char *funcname, int errormask)
{
// 实现所有标准 NumPy 函数的归约逻辑,根据需要调用适当的内部循环函数
// 返回归约结果的数组对象
NPY_NO_EXPORT PyArrayObject *
// 执行归约操作的上下文
context,
// 要归约的数组
* operand,
// 存放结果的数组,如果为 NULL 则在函数内部创建
* out,
// 状态掩码,目前不支持,为了未来兼容性保留
* wheremask,
// 操作数的数据类型,内部循环期望的类型
* operand_dtype,
// 结果的数据类型,内部循环期望的类型
* result_dtype,
// 应用于操作数的类型转换规则
* casting,
// 标志指示 'operand' 的归约轴
* axis_flags,
// 如果为 True,保留结果中尺寸为一的归约维度
keepdims,
// 如果为 Py_None,使用 PyArray_CopyInitialReduceValues 初始化结果
* identity,
// 执行归约的循环
* loop,
// 传递给内部循环的数据
* data,
// 迭代器的缓冲区大小,默认为 0
buffersize,
// 归约函数的名称,用于错误消息
* funcname,
// 从 _get_bufsize_errmask 转发的错误掩码
* errormask);
}
.\numpy\numpy\_core\src\umath\special_integer_comparisons.cpp
/*
* 包含 Python.h 头文件,提供 Python C API 功能支持。
*/
/*
* 避免使用过时的 NumPy API,并设置使用的 NumPy API 版本。
*/
/*
* 包含 NumPy 的相关头文件,定义了 ndarray 类型、数学运算、通用函数等。
*/
/*
* 包含抽象数据类型、分派功能、数据类型元信息、数据类型转换等相关头文件。
*/
/*
* 包含 legacy_array_method.h 头文件,用于获取 legacy ufunc 循环的包装函数。
*/
/*
* 包含 special_integer_comparisons.h 头文件,用于特殊整数比较的函数。
*/
/*
* 用于模板中的辅助函数,避免未覆盖的 switch 分支导致的警告。
*/
enum class COMP {
EQ, NE, LT, LE, GT, GE,
};
/*
* 根据 COMP 枚举值返回对应的比较名称字符串。
*/
static char const *
comp_name(COMP comp) {
switch(comp) {
case COMP::EQ: return "equal";
case COMP::NE: return "not_equal";
case COMP::LT: return "less";
case COMP::LE: return "less_equal";
case COMP::GT: return "greater";
case COMP::GE: return "greater_equal";
default:
assert(0); // 如果出现未知的 COMP 值,触发断言错误。
return nullptr;
}
}
/*
* 模板函数,根据 result 参数设置输出数组的布尔值结果。
* 返回值为 0 表示成功执行。
*/
template <bool result>
static int
fixed_result_loop(PyArrayMethod_Context *NPY_UNUSED(context),
char *const data[], npy_intp const dimensions[],
npy_intp const strides[], NpyAuxData *NPY_UNUSED(auxdata))
{
npy_intp N = dimensions[0]; // 获取数组的第一个维度大小
char *out = data[2]; // 获取输出数组的起始地址
npy_intp stride = strides[2]; // 获取输出数组的步幅
while (N--) {
*reinterpret_cast<npy_bool *>(out) = result; // 将 result 赋值给输出数组的当前元素
out += stride; // 移动到下一个输出数组元素
}
return 0; // 返回表示成功执行
}
/*
* 内联函数,根据数据类型 typenum 获取其最小值和最大值。
*/
static inline void
get_min_max(int typenum, long long *min, unsigned long long *max)
{
*min = 0; // 默认最小值为 0
switch (typenum) {
case NPY_BYTE:
*min = NPY_MIN_BYTE; // 设置为 NPY_BYTE 类型的最小值
*max = NPY_MAX_BYTE; // 设置为 NPY_BYTE 类型的最大值
break;
case NPY_UBYTE:
*max = NPY_MAX_UBYTE; // 设置为 NPY_UBYTE 类型的最大值
break;
case NPY_SHORT:
*min = NPY_MIN_SHORT; // 设置为 NPY_SHORT 类型的最小值
*max = NPY_MAX_SHORT; // 设置为 NPY_SHORT 类型的最大值
break;
case NPY_USHORT:
*max = NPY_MAX_USHORT; // 设置为 NPY_USHORT 类型的最大值
break;
case NPY_INT:
*min = NPY_MIN_INT; // 设置为 NPY_INT 类型的最小值
*max = NPY_MAX_INT; // 设置为 NPY_INT 类型的最大值
break;
case NPY_UINT:
*max = NPY_MAX_UINT; // 设置为 NPY_UINT 类型的最大值
break;
case NPY_LONG:
*min = NPY_MIN_LONG; // 设置为 NPY_LONG 类型的最小值
*max = NPY_MAX_LONG; // 设置为 NPY_LONG 类型的最大值
break;
case NPY_ULONG:
*max = NPY_MAX_ULONG; // 设置为 NPY_ULONG 类型的最大值
break;
case NPY_LONGLONG:
*min = NPY_MIN_LONGLONG; // 设置为 NPY_LONGLONG 类型的最小值
*max = NPY_MAX_LONGLONG; // 设置为 NPY_LONGLONG 类型的最大值
break;
case NPY_ULONGLONG:
*max = NPY_MAX_ULONGLONG; // 设置为 NPY_ULONGLONG 类型的最大值
break;
default:
*max = 0; // 默认最大值为 0
assert(0); // 如果出现未知的 typenum 值,触发断言错误
}
}
/*
* 内联函数,检查 Python 的长整型对象是否在给定类型的范围内。
* 返回 -1 表示出错。
*/
static inline int
get_value_range(PyObject *value, int type_num, int *range)
{
long long min;
unsigned long long max;
get_min_max(type_num, &min, &max); // 获取指定类型的最小值和最大值
int overflow;
long long val = PyLong_AsLongLongAndOverflow(value, &overflow); // 将 Python 长整型转换为 long long
if (val == -1 && overflow == 0 && PyErr_Occurred()) {
return -1; // 如果转换出错,返回 -1
}
// 如果转换成功,检查值是否在指定类型的范围内
return 0; // 返回表示成功执行
}
// 如果溢出标志为0
if (overflow == 0) {
// 如果值小于最小值,设置范围为-1
if (val < min) {
*range = -1;
}
// 如果值大于0且大于最大值,设置范围为1
else if (val > 0 && (unsigned long long)val > max) {
*range = 1;
}
// 否则,设置范围为0
else {
*range = 0;
}
}
// 如果溢出标志为负数,设置范围为-1
else if (overflow < 0) {
*range = -1;
}
// 如果最大值小于等于最大长长整数值
else if (max <= NPY_MAX_LONGLONG) {
// 设置范围为1
*range = 1;
}
// 否则,处理大于长长整数的情况
else {
/*
* 如果我们正在检查无符号长长整数,则值可能大于长长整数,
* 但在无符号长长整数的范围内。通过正常的Python整数比较来检查这一点。
*/
// 创建一个无符号长长整数的Python对象
PyObject *obj = PyLong_FromUnsignedLongLong(max);
// 如果对象创建失败,返回-1
if (obj == NULL) {
return -1;
}
// 进行Python对象的大于比较
int cmp = PyObject_RichCompareBool(value, obj, Py_GT);
Py_DECREF(obj);
// 如果比较出错,返回-1
if (cmp < 0) {
return -1;
}
// 根据比较结果设置范围:1表示大于,0表示否
if (cmp) {
*range = 1;
}
else {
*range = 0;
}
}
// 返回处理结果,成功返回0
return 0;
/*
* Find the type resolution for any numpy_int with pyint comparison. This
* function supports *both* directions for all types.
*/
static NPY_CASTING
resolve_descriptors_with_scalars(
PyArrayMethodObject *self, PyArray_DTypeMeta **dtypes,
PyArray_Descr **given_descrs, PyObject *const *input_scalars,
PyArray_Descr **loop_descrs, npy_intp *view_offset)
{
// 初始化变量 value_range 为 0
int value_range = 0;
// 检查第一个 dtypes 是否为 PyArray_PyLongDType 类型
npy_bool first_is_pyint = dtypes[0] == &PyArray_PyLongDType;
// 根据 first_is_pyint 的值选择数组和标量的索引
int arr_idx = first_is_pyint ? 1 : 0;
int scalar_idx = first_is_pyint ? 0 : 1;
// 获取输入的标量
PyObject *scalar = input_scalars[scalar_idx];
// 断言数组的类型是整数类型
assert(PyTypeNum_ISINTEGER(dtypes[arr_idx]->type_num));
// 获取数组的数据类型元数据
PyArray_DTypeMeta *arr_dtype = dtypes[arr_idx];
/*
* Three way decision (with hack) on value range:
* 0: The value fits within the range of the dtype.
* 1: The value came second and is larger or came first and is smaller.
* -1: The value came second and is smaller or came first and is larger
*/
// 如果标量不为空且是精确的长整型
if (scalar != NULL && PyLong_CheckExact(scalar)) {
// 获取标量值的范围,并根据结果调整 value_range
if (get_value_range(scalar, arr_dtype->type_num, &value_range) < 0) {
return _NPY_ERROR_OCCURRED_IN_CAST;
}
// 如果 first_is_pyint 为真,则将 value_range 取反
if (first_is_pyint == 1) {
value_range *= -1;
}
}
/*
* Very small/large values always need to be encoded as `object` dtype
* in order to never fail casting (NumPy will store the Python integer
* in a 0-D object array this way -- even if we never inspect it).
*
* TRICK: We encode the value range by whether or not we use the object
* singleton! This information is then available in `get_loop()`
* to pick a loop that returns always True or False.
*/
// 根据 value_range 的值选择相应的描述符类型
if (value_range == 0) {
// 对于范围为 0 的情况,使用数组数据类型的 singleton
Py_INCREF(arr_dtype->singleton);
loop_descrs[scalar_idx] = arr_dtype->singleton;
}
else if (value_range < 0) {
// 对于范围小于 0 的情况,使用 NPY_OBJECT 类型的描述符
loop_descrs[scalar_idx] = PyArray_DescrFromType(NPY_OBJECT);
}
else {
// 对于范围大于 0 的情况,创建一个新的 NPY_OBJECT 类型的描述符
loop_descrs[scalar_idx] = PyArray_DescrNewFromType(NPY_OBJECT);
if (loop_descrs[scalar_idx] == NULL) {
return _NPY_ERROR_OCCURRED_IN_CAST;
}
}
// 增加数组数据类型的 singleton 的引用计数
Py_INCREF(arr_dtype->singleton);
// 将数组数据类型的 singleton 设置为数组的描述符
loop_descrs[arr_idx] = arr_dtype->singleton;
// 将 NP_BOOL 类型的描述符设置为第三个描述符
loop_descrs[2] = PyArray_DescrFromType(NPY_BOOL);
// 返回无需转换的状态
return NPY_NO_CASTING;
}
/*
* Template function to retrieve a loop based on the comparison type.
*/
template<COMP comp>
static int
get_loop(PyArrayMethod_Context *context,
int aligned, int move_references, const npy_intp *strides,
PyArrayMethod_StridedLoop **out_loop, NpyAuxData **out_transferdata,
NPY_ARRAYMETHOD_FLAGS *flags)
{
// 如果第一个描述符的类型与第二个相同,则回退到旧的循环实现
if (context->descriptors[1]->type_num == context->descriptors[0]->type_num) {
/*
* Fall back to the current implementation, which wraps legacy loops.
*/
return get_wrapped_legacy_ufunc_loop(
context, aligned, move_references, strides,
out_loop, out_transferdata, flags);
}
}
// 否则情况的处理分支,开始
else {
// 定义指向另一个描述符的指针
PyArray_Descr *other_descr;
// 如果第二个描述符的类型是 NPY_OBJECT,则将其赋给 other_descr
if (context->descriptors[1]->type_num == NPY_OBJECT) {
other_descr = context->descriptors[1];
}
// 否则,假设第一个描述符的类型是 NPY_OBJECT
else {
assert(context->descriptors[0]->type_num == NPY_OBJECT);
other_descr = context->descriptors[0];
}
// HACK: 如果描述符是单例,则结果较小
// 创建一个指向 NPY_OBJECT 类型的单例描述符对象
PyArray_Descr *obj_singleton = PyArray_DescrFromType(NPY_OBJECT);
// 如果 other_descr 和 obj_singleton 相等
if (other_descr == obj_singleton) {
// 根据比较操作类型设置输出循环的指针
switch (comp) {
case COMP::EQ:
case COMP::LT:
case COMP::LE:
*out_loop = &fixed_result_loop<false>;
break;
case COMP::NE:
case COMP::GT:
case COMP::GE:
*out_loop = &fixed_result_loop<true>;
break;
}
}
// 否则,other_descr 和 obj_singleton 不相等
else {
// 根据比较操作类型设置输出循环的指针
switch (comp) {
case COMP::EQ:
case COMP::GT:
case COMP::GE:
*out_loop = &fixed_result_loop<false>;
break;
case COMP::NE:
case COMP::LT:
case COMP::LE:
*out_loop = &fixed_result_loop<true>;
break;
}
}
// 释放单例描述符对象的引用
Py_DECREF(obj_singleton);
}
// 设置标志位,指示无浮点错误
*flags = NPY_METH_NO_FLOATINGPOINT_ERRORS;
// 返回 0 表示成功
return 0;
/*
* 结束函数,返回成功状态码
*/
}
/*
* 用于将 Python 整数添加到 NumPy 整数比较中的机制,
* 以及用于特殊情况下的 Python 整数与 Python 整数比较的特殊推广。
*/
/*
* 简单的推广器,确保在输入仅为 Python 整数时使用对象循环。
* 注意,如果用户显式传递 Python `int` 抽象的 DType,则承诺实际传递的是 Python 整数,
* 我们接受这一点,并不做检查。
*/
static int
pyint_comparison_promoter(PyUFuncObject *NPY_UNUSED(ufunc),
PyArray_DTypeMeta *op_dtypes[], PyArray_DTypeMeta *signature[],
PyArray_DTypeMeta *new_op_dtypes[])
{
new_op_dtypes[0] = NPY_DT_NewRef(&PyArray_ObjectDType); // 第一个操作数类型设为对象类型
new_op_dtypes[1] = NPY_DT_NewRef(&PyArray_ObjectDType); // 第二个操作数类型设为对象类型
new_op_dtypes[2] = NPY_DT_NewRef(&PyArray_BoolDType); // 结果类型设为布尔类型
return 0; // 返回成功状态码
}
/*
* 此函数使用传入的循环替换步进循环,并将其注册到给定的 ufunc 中。
* 它还为 (pyint, pyint, bool) 添加推广器,以使用 (object, object, bool) 实现。
*/
template<COMP comp>
static int
add_dtype_loops(PyObject *umath, PyArrayMethod_Spec *spec, PyObject *info)
{
PyArray_DTypeMeta *PyInt = &PyArray_PyLongDType; // Python 整数类型
PyObject *name = PyUnicode_FromString(comp_name(comp));
if (name == nullptr) { // 如果创建名称对象失败
return -1; // 返回错误状态码
}
PyUFuncObject *ufunc = (PyUFuncObject *)PyObject_GetItem(umath, name); // 获取 ufunc 对象
Py_DECREF(name); // 释放名称对象的引用计数
if (ufunc == nullptr) { // 如果获取 ufunc 对象失败
return -1; // 返回错误状态码
}
if (Py_TYPE(ufunc) != &PyUFunc_Type) { // 如果 ufunc 对象类型不是 PyUFunc_Type
PyErr_SetString(PyExc_RuntimeError,
"internal NumPy error: comparison not a ufunc"); // 设置错误信息
goto fail; // 跳转到失败处理代码块
}
/*
* 注意:迭代所有类型号码,希望减少此次迭代。
* (如果我们总体上统一 int DTypes 将更容易。)
*/
for (int typenum = NPY_BYTE; typenum <= NPY_ULONGLONG; typenum++) {
spec->slots[0].pfunc = (void *)get_loop<comp>; // 设置特定比较操作的循环函数
PyArray_DTypeMeta *Int = PyArray_DTypeFromTypeNum(typenum); // 根据类型号码获取类型对象
/* 注册正向和反向方向的 spec/loop */
spec->dtypes[0] = Int; // 第一个操作数类型设为当前类型对象
spec->dtypes[1] = PyInt; // 第二个操作数类型设为 Python 整数类型
int res = PyUFunc_AddLoopFromSpec_int((PyObject *)ufunc, spec, 1); // 添加 spec/loop 到 ufunc
if (res < 0) {
Py_DECREF(Int); // 减少类型对象的引用计数
goto fail; // 跳转到失败处理代码块
}
spec->dtypes[0] = PyInt; // 第一个操作数类型设为 Python 整数类型
spec->dtypes[1] = Int; // 第二个操作数类型设为当前类型对象
res = PyUFunc_AddLoopFromSpec_int((PyObject *)ufunc, spec, 1); // 添加 spec/loop 到 ufunc
Py_DECREF(Int); // 减少类型对象的引用计数
if (res < 0) {
goto fail; // 跳转到失败处理代码块
}
}
/*
* 安装推广信息以允许两个 Python 整数进行比较。
*/
return PyUFunc_AddLoop((PyUFuncObject *)ufunc, info, 0); // 添加循环到 ufunc 并返回成功状态码
Py_DECREF(ufunc); // 减少 ufunc 对象的引用计数
return 0; // 返回成功状态码
fail:
Py_DECREF(ufunc); // 减少 ufunc 对象的引用计数
return -1; // 返回错误状态码
}
template<COMP...>
struct add_loops;
template<>
struct add_loops<> {
int operator()(PyObject*, PyArrayMethod_Spec*, PyObject *) {
return 0; // 返回成功状态码
}
};
/*
* 定义模板结构体 `add_loops`,接受多个比较器作为模板参数,并重载函数调用操作符
*/
struct add_loops<comp, comps...> {
/*
* 重载函数调用操作符,接受三个指针参数,并返回整数结果
*/
int operator()(PyObject* umath, PyArrayMethod_Spec* spec, PyObject *info) {
// 如果调用 `add_dtype_loops` 失败,返回 -1
if (add_dtype_loops<comp>(umath, spec, info) < 0) {
return -1;
}
else {
// 递归调用 `add_loops` 结构体的实例
return add_loops<comps...>()(umath, spec, info);
}
}
};
/*
* 初始化特殊整数比较操作
*/
NPY_NO_EXPORT int
init_special_int_comparisons(PyObject *umath)
{
int res = -1;
PyObject *info = NULL, *promoter = NULL;
PyArray_DTypeMeta *Bool = &PyArray_BoolDType;
/* 所有循环具有布尔输出 DType(其它类型稍后填充) */
PyArray_DTypeMeta *dtypes[] = {NULL, NULL, Bool};
/*
* 目前我们只有一个循环,即步进循环。默认类型解析器确保原生字节顺序/规范表示。
*/
PyType_Slot slots[] = {
{NPY_METH_get_loop, nullptr},
{_NPY_METH_resolve_descriptors_with_scalars,
(void *)&resolve_descriptors_with_scalars},
{0, NULL},
};
PyArrayMethod_Spec spec = {};
spec.name = "templated_pyint_to_integers_comparisons";
spec.nin = 2;
spec.nout = 1;
spec.dtypes = dtypes;
spec.slots = slots;
spec.flags = NPY_METH_NO_FLOATINGPOINT_ERRORS;
/*
* 以下设置正确的促进器,以使类似 `np.equal(2, 4)`(使用两个 Python 整数)的比较使用对象循环。
*/
PyObject *dtype_tuple = PyTuple_Pack(3,
&PyArray_PyLongDType, &PyArray_PyLongDType, Bool);
if (dtype_tuple == NULL) {
goto finish;
}
promoter = PyCapsule_New(
(void *)&pyint_comparison_promoter, "numpy._ufunc_promoter", NULL);
if (promoter == NULL) {
Py_DECREF(dtype_tuple);
goto finish;
}
info = PyTuple_Pack(2, dtype_tuple, promoter);
Py_DECREF(dtype_tuple);
Py_DECREF(promoter);
if (info == NULL) {
goto finish;
}
/* 添加所有 PyInt 和 NumPy 整数比较的组合 */
using comp_looper = add_loops<COMP::EQ, COMP::NE, COMP::LT, COMP::LE, COMP::GT, COMP::GE>;
if (comp_looper()(umath, &spec, info) < 0) {
goto finish;
}
res = 0;
finish:
Py_XDECREF(info);
return res;
}
.\numpy\numpy\_core\src\umath\special_integer_comparisons.h
// 如果 _NPY_CORE_SRC_UMATH_SPECIAL_COMPARISONS_H_ 宏未定义,则执行以下内容
// 定义 _NPY_CORE_SRC_UMATH_SPECIAL_COMPARISONS_H_ 宏,避免重复包含
// 如果编译器为 C++,则进行 C++ 的链接处理
extern "C" {
// 开始 extern "C" 块,确保链接符合 C++ 格式的要求
NPY_NO_EXPORT int
// 声明一个不导出的 int 类型函数,NPY_NO_EXPORT 是一个宏,用于指示不导出函数
init_special_int_comparisons(PyObject *umath);
// 函数原型声明,初始化特殊整数比较函数,接受一个 PyObject 类型指针作为参数
// 如果编译器为 C++,结束 C++ 的链接处理
}
// 结束 extern "C" 块
// 结束宏定义,确保头文件内容完整,并使用宏定义的名称作为结束注释
.\numpy\numpy\_core\src\umath\stringdtype_ufuncs.cpp
/* Ufunc implementations for the StringDType class */
/* Define macro LOAD_TWO_INPUT_STRINGS(CONTEXT) for loading and checking two input strings */
const npy_packed_static_string *ps1 = (npy_packed_static_string *)in1; \
npy_static_string s1 = {0, NULL}; \
int s1_isnull = NpyString_load(s1allocator, ps1, &s1); \
const npy_packed_static_string *ps2 = (npy_packed_static_string *)in2; \
npy_static_string s2 = {0, NULL}; \
int s2_isnull = NpyString_load(s2allocator, ps2, &s2); \
if (s1_isnull == -1 || s2_isnull == -1) { \
npy_gil_error(PyExc_MemoryError, "Failed to load string in %s", \
CONTEXT); \
goto fail; \
} \
/* Implementation of a function to resolve descriptors for multiplication */
static NPY_CASTING
multiply_resolve_descriptors(
struct PyArrayMethodObject_tag *NPY_UNUSED(method),
PyArray_DTypeMeta *const dtypes[], PyArray_Descr *const given_descrs[],
PyArray_Descr *loop_descrs[], npy_intp *NPY_UNUSED(view_offset))
{
PyArray_Descr *ldescr = given_descrs[0]; // Left input descriptor
PyArray_Descr *rdescr = given_descrs[1]; // Right input descriptor
PyArray_StringDTypeObject *odescr = NULL; // Output string dtype descriptor
PyArray_Descr *out_descr = NULL; // Output descriptor
// Determine which dtype (left or right) corresponds to PyArray_StringDType
if (dtypes[0] == &PyArray_StringDType) {
odescr = (PyArray_StringDTypeObject *)ldescr;
}
else {
odescr = (PyArray_StringDTypeObject *)rdescr;
}
// If the third descriptor is not provided, create a new instance of string dtype
if (given_descrs[2] == NULL) {
out_descr = (PyArray_Descr *)new_stringdtype_instance(
odescr->na_object, odescr->coerce);
if (out_descr == NULL) {
return (NPY_CASTING)-1; // Return error if creation fails
}
}
else {
Py_INCREF(given_descrs[2]);
out_descr = given_descrs[2];
}
// Increment references to input descriptors and assign to loop descriptors
Py_INCREF(ldescr);
loop_descrs[0] = ldescr;
Py_INCREF(rdescr);
loop_descrs[1] = rdescr;
loop_descrs[2] = out_descr; // Assign output descriptor to loop descriptors array
return NPY_NO_CASTING; // Return no-casting flag
}
static int multiply_loop_core(
npy_intp N, char *sin, char *iin, char *out,
npy_intp s_stride, npy_intp i_stride, npy_intp o_stride,
PyArray_StringDTypeObject *idescr, PyArray_StringDTypeObject *odescr)
{
PyArray_Descr *descrs[2] =
{(PyArray_Descr *)idescr, (PyArray_Descr *)odescr};
npy_string_allocator *allocators[2] = {};
NpyString_acquire_allocators(2, descrs, allocators);
npy_string_allocator *iallocator = allocators[0];
npy_string_allocator *oallocator = allocators[1];
int has_null = idescr->na_object != NULL;
int has_nan_na = idescr->has_nan_na;
int has_string_na = idescr->has_string_na;
const npy_static_string *default_string = &idescr->default_string;
// 循环 N 次,处理输入和输出的字符串数据
while (N--) {
// 从输入源读取一个静态压缩字符串结构到 ips
const npy_packed_static_string *ips =
(npy_packed_static_string *)sin;
// 初始化输出的静态字符串结构 is
npy_static_string is = {0, NULL};
// 从输出源读取一个静态压缩字符串结构到 ops
npy_packed_static_string *ops = (npy_packed_static_string *)out;
// 载入 ips 所指向的数据到 is,如果失败返回 is_isnull
int is_isnull = NpyString_load(iallocator, ips, &is);
// 如果载入过程中出现内存错误,报错并跳转到 fail 标签
if (is_isnull == -1) {
npy_gil_error(PyExc_MemoryError,
"Failed to load string in multiply");
goto fail;
}
// 如果载入结果为空值
else if (is_isnull) {
// 如果允许 NaN 或 NA 值存在
if (has_nan_na) {
// 将空值打包到 ops 中,如果失败则报错并跳转到 fail 标签
if (NpyString_pack_null(oallocator, ops) < 0) {
npy_gil_error(PyExc_MemoryError,
"Failed to deallocate string in multiply");
goto fail;
}
// 调整输入和输出指针以及步长,并继续下一次循环
sin += s_stride;
iin += i_stride;
out += o_stride;
continue;
}
// 如果存在字符串的 NA 值或者不允许空值存在
else if (has_string_na || !has_null) {
// 使用默认字符串作为 is 的值
is = *(npy_static_string *)default_string;
}
// 否则报类型错误并跳转到 fail 标签
else {
npy_gil_error(PyExc_TypeError,
"Cannot multiply null that is not a nan-like "
"value");
goto fail;
}
}
// 从输入中读取因子值到 factor
T factor = *(T *)iin;
// 计算新的字符串大小,检查是否溢出
size_t cursize = is.size;
size_t newsize;
int overflowed = npy_mul_with_overflow_size_t(
&newsize, cursize, factor);
// 如果溢出,报内存错误并跳转到 fail 标签
if (overflowed) {
npy_gil_error(PyExc_MemoryError,
"Failed to allocate string in string multiply");
goto fail;
}
char *buf = NULL;
npy_static_string os = {0, NULL};
// 如果描述符相同,执行原地操作
if (descrs[0] == descrs[1]) {
// 分配新的缓冲区以存储扩展后的字符串
buf = (char *)PyMem_RawMalloc(newsize);
// 如果分配失败,报内存错误并跳转到 fail 标签
if (buf == NULL) {
npy_gil_error(PyExc_MemoryError,
"Failed to allocate string in multiply");
goto fail;
}
}
// 如果描述符不同,加载新的字符串到 os 中
else {
// 加载新字符串到 ops 中,如果失败则跳转到 fail 标签
if (load_new_string(
ops, &os, newsize,
oallocator, "multiply") == -1) {
goto fail;
}
// 将新缓冲区初始化为 os.buf
/* explicitly discard const; initializing new buffer */
buf = (char *)os.buf;
}
// 将 is.buf 的内容复制 factor 次到 buf 中
for (size_t i = 0; i < (size_t)factor; i++) {
/* multiply can't overflow because cursize * factor */
/* has already been checked and doesn't overflow */
memcpy((char *)buf + i * cursize, is.buf, cursize);
}
// 如果描述符相同,进行原地打包操作
if (descrs[0] == descrs[1]) {
// 将 buf 中的数据打包到 ops 中,如果失败则报内存错误并跳转到 fail 标签
if (NpyString_pack(oallocator, ops, buf, newsize) < 0) {
npy_gil_error(PyExc_MemoryError,
"Failed to pack string in multiply");
goto fail;
}
// 释放临时缓冲区 buf
PyMem_RawFree(buf);
}
// 调整输入和输出指针以及步长,准备下一轮循环
sin += s_stride;
iin += i_stride;
out += o_stride;
}
// 调用 NpyString_release_allocators 函数,释放内存分配器资源,参数为 2 和 allocators 数组
NpyString_release_allocators(2, allocators);
// 返回整数值 0,表示函数执行成功
return 0;
fail:
NpyString_release_allocators(2, allocators);
return -1;
}
template <typename T>
static int multiply_right_strided_loop(
PyArrayMethod_Context *context, char *const data[],
npy_intp const dimensions[], npy_intp const strides[],
NpyAuxData *NPY_UNUSED(auxdata))
{
// 获取输入和输出的数据类型描述符
PyArray_StringDTypeObject *idescr =
(PyArray_StringDTypeObject *)context->descriptors[0];
PyArray_StringDTypeObject *odescr =
(PyArray_StringDTypeObject *)context->descriptors[2];
// 获取数组的长度
npy_intp N = dimensions[0];
// 获取输入数组的指针
char *sin = data[0];
char *iin = data[1];
char *out = data[2];
// 获取输入数组的步长
npy_intp in1_stride = strides[0];
npy_intp in2_stride = strides[1];
npy_intp out_stride = strides[2];
// 调用模板函数 multiply_loop_core 进行核心的乘法运算
return multiply_loop_core<T>(
N, sin, iin, out, in1_stride, in2_stride, out_stride,
idescr, odescr);
}
template <typename T>
static int multiply_left_strided_loop(
PyArrayMethod_Context *context, char *const data[],
npy_intp const dimensions[], npy_intp const strides[],
NpyAuxData *NPY_UNUSED(auxdata))
{
// 获取输入和输出的数据类型描述符
PyArray_StringDTypeObject *idescr =
(PyArray_StringDTypeObject *)context->descriptors[1];
PyArray_StringDTypeObject *odescr =
(PyArray_StringDTypeObject *)context->descriptors[2];
// 获取数组的长度
npy_intp N = dimensions[0];
// 获取输入数组的指针
char *iin = data[0];
char *sin = data[1];
char *out = data[2];
// 获取输入数组的步长
npy_intp in1_stride = strides[0];
npy_intp in2_stride = strides[1];
npy_intp out_stride = strides[2];
// 调用模板函数 multiply_loop_core 进行核心的乘法运算
return multiply_loop_core<T>(
N, sin, iin, out, in2_stride, in1_stride, out_stride,
idescr, odescr);
}
static NPY_CASTING
binary_resolve_descriptors(struct PyArrayMethodObject_tag *NPY_UNUSED(method),
PyArray_DTypeMeta *const NPY_UNUSED(dtypes[]),
PyArray_Descr *const given_descrs[],
PyArray_Descr *loop_descrs[],
npy_intp *NPY_UNUSED(view_offset))
{
// 获取给定的输入数据类型描述符
PyArray_StringDTypeObject *descr1 = (PyArray_StringDTypeObject *)given_descrs[0];
PyArray_StringDTypeObject *descr2 = (PyArray_StringDTypeObject *)given_descrs[1];
// 判断是否需要强制转换输出
int out_coerce = descr1->coerce && descr1->coerce;
PyObject *out_na_object = NULL;
// 检查字符串类型描述符的兼容性,如果不兼容则返回 -1
if (stringdtype_compatible_na(
descr1->na_object, descr2->na_object, &out_na_object) == -1) {
return (NPY_CASTING)-1;
}
// 增加输入描述符的引用计数,并设置循环描述符的值
Py_INCREF(given_descrs[0]);
loop_descrs[0] = given_descrs[0];
// 增加输入描述符的引用计数,并设置循环描述符的值
Py_INCREF(given_descrs[1]);
loop_descrs[1] = given_descrs[1];
PyArray_Descr *out_descr = NULL;
// 如果没有给定输出描述符,则创建一个新的字符串类型实例
if (given_descrs[2] == NULL) {
out_descr = (PyArray_Descr *)new_stringdtype_instance(
out_na_object, out_coerce);
// 如果创建失败,则返回 -1
if (out_descr == NULL) {
return (NPY_CASTING)-1;
}
}
else {
// 增加给定输出描述符的引用计数,并设置循环描述符的值
Py_INCREF(given_descrs[2]);
loop_descrs[2] = given_descrs[2];
}
// 设置输出描述符的值
loop_descrs[2] = out_descr;
// 返回不需要任何强制转换
return NPY_NO_CASTING;
}
static int
add_strided_loop(PyArrayMethod_Context *context, char *const data[],
npy_intp const dimensions[], npy_intp const strides[],
NpyAuxData *NPY_UNUSED(auxdata))
{
PyArray_StringDTypeObject *s1descr = (PyArray_StringDTypeObject *)context->descriptors[0];
PyArray_StringDTypeObject *s2descr = (PyArray_StringDTypeObject *)context->descriptors[1];
PyArray_StringDTypeObject *odescr = (PyArray_StringDTypeObject *)context->descriptors[2];
int has_null = s1descr->na_object != NULL;
int has_nan_na = s1descr->has_nan_na;
int has_string_na = s1descr->has_string_na;
const npy_static_string *default_string = &s1descr->default_string;
npy_intp N = dimensions[0];
char *in1 = data[0];
char *in2 = data[1];
char *out = data[2];
npy_intp in1_stride = strides[0];
npy_intp in2_stride = strides[1];
npy_intp out_stride = strides[2];
npy_string_allocator *allocators[3] = {};
NpyString_acquire_allocators(3, context->descriptors, allocators);
npy_string_allocator *s1allocator = allocators[0];
npy_string_allocator *s2allocator = allocators[1];
npy_string_allocator *oallocator = allocators[2];
// 循环执行 N 次,每次执行一组操作
while (N--) {
// 宏定义:加载两个输入字符串,操作为"add"
LOAD_TWO_INPUT_STRINGS("add")
// 初始化变量
char *buf = NULL;
npy_static_string os = {0, NULL};
size_t newsize = 0;
npy_packed_static_string *ops = (npy_packed_static_string *)out;
// 检查是否存在空值
if (NPY_UNLIKELY(s1_isnull || s2_isnull)) {
// 如果存在 NaN 或 NA,将空字符串打包到输出
if (has_nan_na) {
if (NpyString_pack_null(oallocator, ops) < 0) {
// 内存错误处理:打包字符串失败
npy_gil_error(PyExc_MemoryError,
"Failed to deallocate string in add");
goto fail;
}
// 跳转到下一步骤
goto next_step;
}
// 如果存在字符串 NA 或没有空值
else if (has_string_na || !has_null) {
// 如果 s1 为空,则使用默认字符串
if (s1_isnull) {
s1 = *default_string;
}
// 如果 s2 为空,则使用默认字符串
if (s2_isnull) {
s2 = *default_string;
}
}
// 否则,出现不支持的空值情况,抛出值错误
else {
npy_gil_error(PyExc_ValueError,
"Cannot add null that is not a nan-like value");
goto fail;
}
}
// 检查是否会溢出
newsize = s1.size + s2.size;
if (newsize < s1.size) {
// 内存错误处理:分配字符串失败
npy_gil_error(PyExc_MemoryError, "Failed to allocate string in add");
goto fail;
}
// 如果是原地操作
if (s1descr == odescr || s2descr == odescr) {
// 分配内存
buf = (char *)PyMem_RawMalloc(newsize);
if (buf == NULL) {
// 内存错误处理:分配字符串失败
npy_gil_error(PyExc_MemoryError,
"Failed to allocate string in add");
goto fail;
}
}
// 否则,加载新字符串并初始化新缓冲区
else {
if (load_new_string(ops, &os, newsize, oallocator, "add") == -1) {
goto fail;
}
// 显式丢弃 const;初始化新缓冲区
buf = (char *)os.buf;
}
// 复制 s1 和 s2 的内容到 buf
memcpy(buf, s1.buf, s1.size);
memcpy(buf + s1.size, s2.buf, s2.size);
// 清理临时的原地缓冲区
if (s1descr == odescr || s2descr == odescr) {
// 将 buf 打包到输出字符串
if (NpyString_pack(oallocator, ops, buf, newsize) < 0) {
// 内存错误处理:打包输出字符串失败
npy_gil_error(PyExc_MemoryError,
"Failed to pack output string in add");
goto fail;
}
// 释放内存
PyMem_RawFree(buf);
}
next_step:
// 更新输入和输出指针位置
in1 += in1_stride;
in2 += in2_stride;
out += out_stride;
}
// 释放所有分配器
NpyString_release_allocators(3, allocators);
// 返回成功状态
return 0;
// 根据输入的上下文和数据执行字符串比较操作的循环
static int
string_comparison_strided_loop(PyArrayMethod_Context *context, char *const data[],
npy_intp const dimensions[],
npy_intp const strides[],
NpyAuxData *NPY_UNUSED(auxdata))
{
// 获取调用该函数的通用函数对象的名称
const char *ufunc_name = ((PyUFuncObject *)context->caller)->name;
// 从静态数据中提取等于操作的结果标志
npy_bool res_for_eq = ((npy_bool *)context->method->static_data)[0];
// 从静态数据中提取小于操作的结果标志
npy_bool res_for_lt = ((npy_bool *)context->method->static_data)[1];
// 获取第一个输入参数的字符串数据类型描述符
PyArray_StringDTypeObject *in1_descr =
((PyArray_StringDTypeObject *)context->descriptors[0]);
// 获取第二个输入参数的字符串数据类型描述符
PyArray_StringDTypeObject *in2_descr =
((PyArray_StringDTypeObject *)context->descriptors[1]);
// 获取当前处理的维度大小
npy_intp N = dimensions[0];
// 获取第一个输入参数的起始地址
char *in1 = data[0];
// 获取第二个输入参数的起始地址
char *in2 = data[1];
// 获取输出参数的起始地址
char *out = data[2];
// 获取第一个输入参数的步长
npy_intp in1_stride = strides[0];
// 获取第二个输入参数的步长
npy_intp in2_stride = strides[1];
// 获取输出参数的步长
npy_intp out_stride = strides[2];
// 创建用于存储字符串分配器的数组
npy_string_allocator *allocators[3] = {};
// 获取字符串分配器,用于输入和输出参数的内存分配
NpyString_acquire_allocators(3, context->descriptors, allocators);
// 获取第一个输入参数的字符串分配器
npy_string_allocator *in1_allocator = allocators[0];
// 获取第二个输入参数的字符串分配器
npy_string_allocator *in2_allocator = allocators[1];
// 获取输出参数的字符串分配器
npy_string_allocator *out_allocator = allocators[2];
// 循环处理每个元素
while (N--) {
// 将第一个输入参数转换为静态字符串
const npy_packed_static_string *sin1 = (npy_packed_static_string *)in1;
// 将第二个输入参数转换为静态字符串
const npy_packed_static_string *sin2 = (npy_packed_static_string *)in2;
// 将输出参数转换为静态字符串
npy_packed_static_string *sout = (npy_packed_static_string *)out;
// 执行字符串的比较操作
int cmp = _compare(in1, in2, in1_descr, in2_descr);
// 如果字符串相等且输出等于输入之一,则跳到下一步
if (cmp == 0 && (in1 == out || in2 == out)) {
goto next_step;
}
// 根据比较结果和操作标志执行复制操作或避免释放后使用
if ((cmp < 0) ^ res_for_lt) {
// 如果输入1不等于输出,则执行复制操作
if (in1 != out) {
if (free_and_copy(in1_allocator, out_allocator, sin1, sout,
ufunc_name) == -1) {
goto fail;
}
}
}
else {
// 如果输入2不等于输出,则执行复制操作
if (in2 != out) {
if (free_and_copy(in2_allocator, out_allocator, sin2, sout,
ufunc_name) == -1) {
goto fail;
}
}
}
next_step:
// 更新输入和输出的指针位置
in1 += in1_stride;
in2 += in2_stride;
out += out_stride;
}
// 释放字符串分配器
NpyString_release_allocators(3, allocators);
// 返回成功完成操作的标志
return 0;
fail:
// 在操作失败时释放字符串分配器
NpyString_release_allocators(3, allocators);
// 返回操作失败的标志
return -1;
}
// 从上下文中获取第三个静态数据的布尔值作为大于结果的标志
npy_bool res_for_gt = ((npy_bool *)context->method->static_data)[2];
// 计算不等于结果的布尔值
npy_bool res_for_ne = !res_for_eq;
// 检查是否等于或不等于的结果是相同的
npy_bool eq_or_ne = res_for_lt == res_for_gt;
// 从上下文中获取第一个描述符并转换为字符串数据类型对象
PyArray_StringDTypeObject *descr1 = (PyArray_StringDTypeObject *)context->descriptors[0];
// 检查描述符中是否有 NULL 值
int has_null = descr1->na_object != NULL;
// 检查描述符中是否有 NaN 或 NA 值
int has_nan_na = descr1->has_nan_na;
// 检查描述符中是否有字符串 NA 值
int has_string_na = descr1->has_string_na;
// 获取默认字符串并指向描述符的默认字符串
const npy_static_string *default_string = &descr1->default_string;
// 获取第一个维度的大小
npy_intp N = dimensions[0];
// 获取输入数据数组的第一个指针
char *in1 = data[0];
// 获取输入数据数组的第二个指针
char *in2 = data[1];
// 获取输出数据数组的指针,并转换为布尔类型指针
npy_bool *out = (npy_bool *)data[2];
// 获取输入数据数组的第一个步长
npy_intp in1_stride = strides[0];
// 获取输入数据数组的第二个步长
npy_intp in2_stride = strides[1];
// 获取输出数据数组的步长
npy_intp out_stride = strides[2];
// 分配两个字符串分配器的数组并初始化为 NULL
npy_string_allocator *allocators[2] = {};
// 调用函数以获取字符串分配器
NpyString_acquire_allocators(2, context->descriptors, allocators);
// 获取第一个字符串分配器
npy_string_allocator *s1allocator = allocators[0];
// 获取第二个字符串分配器
npy_string_allocator *s2allocator = allocators[1];
// 循环处理每个元素
while (N--) {
int cmp;
// 载入两个输入字符串,宏定义的函数
LOAD_TWO_INPUT_STRINGS(ufunc_name);
// 检查是否有 NULL 或 NaN NA 值
if (NPY_UNLIKELY(s1_isnull || s2_isnull)) {
// 如果有 NaN NA 值
if (has_nan_na) {
// s1 或 s2 是 NA
*out = NPY_FALSE;
// 跳转到下一步骤
goto next_step;
}
// 如果有 NULL 值但没有字符串 NA 值
else if (has_null && !has_string_na) {
// 如果是等于或不等于操作
if (eq_or_ne) {
// 如果两个都是 NULL
if (s1_isnull && s2_isnull) {
*out = res_for_eq;
}
else {
*out = res_for_ne;
}
}
else {
// 抛出异常,不支持在非 NaN 类型或字符串中的空值
npy_gil_error(PyExc_ValueError,
"'%s' not supported for null values that are not "
"nan-like or strings.", ufunc_name);
// 跳转到失败处理
goto fail;
}
}
// 否则,处理默认字符串
else {
if (s1_isnull) {
s1 = *default_string;
}
if (s2_isnull) {
s2 = *default_string;
}
}
}
// 比较两个字符串
cmp = NpyString_cmp(&s1, &s2);
// 根据比较结果设置输出值
if (cmp == 0) {
*out = res_for_eq;
}
else if (cmp < 0) {
*out = res_for_lt;
}
else {
*out = res_for_gt;
}
next_step:
// 更新输入和输出指针
in1 += in1_stride;
in2 += in2_stride;
out += out_stride;
}
// 释放字符串分配器
NpyString_release_allocators(2, allocators);
// 返回成功状态
return 0;
fail:
NpyString_release_allocators(2, allocators);
return -1;
}
static NPY_CASTING
string_comparison_resolve_descriptors(
struct PyArrayMethodObject_tag *NPY_UNUSED(method),
PyArray_DTypeMeta *const NPY_UNUSED(dtypes[]),
PyArray_Descr *const given_descrs[],
PyArray_Descr *loop_descrs[], npy_intp *NPY_UNUSED(view_offset))
{
PyArray_StringDTypeObject *descr1 = (PyArray_StringDTypeObject *)given_descrs[0];
PyArray_StringDTypeObject *descr2 = (PyArray_StringDTypeObject *)given_descrs[1];
if (stringdtype_compatible_na(descr1->na_object, descr2->na_object, NULL) == -1) {
return (NPY_CASTING)-1;
}
Py_INCREF(given_descrs[0]);
loop_descrs[0] = given_descrs[0];
Py_INCREF(given_descrs[1]);
loop_descrs[1] = given_descrs[1];
loop_descrs[2] = PyArray_DescrFromType(NPY_BOOL);
return NPY_NO_CASTING;
}
static int
string_isnan_strided_loop(PyArrayMethod_Context *context, char *const data[],
npy_intp const dimensions[],
npy_intp const strides[],
NpyAuxData *NPY_UNUSED(auxdata))
{
PyArray_StringDTypeObject *descr = (PyArray_StringDTypeObject *)context->descriptors[0];
int has_nan_na = descr->has_nan_na;
npy_intp N = dimensions[0];
char *in = data[0];
npy_bool *out = (npy_bool *)data[1];
npy_intp in_stride = strides[0];
npy_intp out_stride = strides[1];
while (N--) {
const npy_packed_static_string *s = (npy_packed_static_string *)in;
if (has_nan_na && NpyString_isnull(s)) {
*out = NPY_TRUE;
}
else {
*out = NPY_FALSE;
}
in += in_stride;
out += out_stride;
}
return 0;
}
static NPY_CASTING
string_bool_output_resolve_descriptors(
struct PyArrayMethodObject_tag *NPY_UNUSED(method),
PyArray_DTypeMeta *const NPY_UNUSED(dtypes[]),
PyArray_Descr *const given_descrs[],
PyArray_Descr *loop_descrs[], npy_intp *NPY_UNUSED(view_offset))
{
Py_INCREF(given_descrs[0]);
loop_descrs[0] = given_descrs[0];
loop_descrs[1] = PyArray_DescrFromType(NPY_BOOL);
return NPY_NO_CASTING;
}
static NPY_CASTING
string_intp_output_resolve_descriptors(
struct PyArrayMethodObject_tag *NPY_UNUSED(method),
PyArray_DTypeMeta *const NPY_UNUSED(dtypes[]),
PyArray_Descr *const given_descrs[],
PyArray_Descr *loop_descrs[], npy_intp *NPY_UNUSED(view_offset))
{
Py_INCREF(given_descrs[0]);
loop_descrs[0] = given_descrs[0];
loop_descrs[1] = PyArray_DescrFromType(NPY_INTP);
return NPY_NO_CASTING;
}
using utf8_buffer_method = bool (Buffer<ENCODING::UTF8>::*)();
static int
// 获取调用者的 ufunc 名称
const char *ufunc_name = ((PyUFuncObject *)context->caller)->name;
// 获取 static_data 中的 utf8_buffer_method 结构体
utf8_buffer_method is_it = *(utf8_buffer_method *)(context->method->static_data);
// 获取第一个描述符的字符串数据类型对象
PyArray_StringDTypeObject *descr = (PyArray_StringDTypeObject *)context->descriptors[0];
// 获取字符串分配器
npy_string_allocator *allocator = NpyString_acquire_allocator(descr);
// 检查描述符是否包含字符串 NA
int has_string_na = descr->has_string_na;
// 检查描述符是否包含 NaN NA
int has_nan_na = descr->has_nan_na;
// 获取默认字符串
const npy_static_string *default_string = &descr->default_string;
// 获取第一个维度的大小
npy_intp N = dimensions[0];
// 获取输入数据指针
char *in = data[0];
// 获取输出数据指针
char *out = data[1];
// 获取输入步长
npy_intp in_stride = strides[0];
// 获取输出步长
npy_intp out_stride = strides[1];
// 循环处理每个元素
while (N--) {
// 将输入数据解析为 npy_packed_static_string 结构体
const npy_packed_static_string *ps = (npy_packed_static_string *)in;
// 初始化一个空的静态字符串 s
npy_static_string s = {0, NULL};
// 初始化 buffer 和 size 为 NULL
const char *buffer = NULL;
size_t size = 0;
// 初始化一个 Buffer<ENCODING::UTF8> 对象 buf
Buffer<ENCODING::UTF8> buf;
// 载入字符串到 s 中,返回是否为 null
int is_null = NpyString_load(allocator, ps, &s);
// 如果载入失败,抛出内存错误并跳转到 fail 标签处
if (is_null == -1) {
npy_gil_error(PyExc_MemoryError, "Failed to load string in %s", ufunc_name);
goto fail;
}
// 如果字符串为 null
if (is_null) {
// 如果描述符允许 NaN NA,则将输出设为 NPY_FALSE 并跳转到 next_step
if (has_nan_na) {
*out = NPY_FALSE;
goto next_step;
}
// 如果不允许字符串 NA,则抛出值错误并跳转到 fail 标签处
else if (!has_string_na) {
npy_gil_error(PyExc_ValueError,
"Cannot use the %s function with a null that is "
"not a nan-like value", ufunc_name);
goto fail;
}
// 否则使用默认字符串的数据
buffer = default_string->buf;
size = default_string->size;
}
else {
// 否则使用载入的字符串数据
buffer = s.buf;
size = s.size;
}
// 初始化 buf 为包含 buffer 和 size 的 UTF8 编码的 Buffer 对象
buf = Buffer<ENCODING::UTF8>((char *)buffer, size);
// 将输出数据转换为 npy_bool 并存储 buf 执行 is_it 操作的结果
*(npy_bool *)out = (buf.*is_it)();
next_step:
// 更新输入和输出指针
in += in_stride;
out += out_stride;
}
// 释放字符串分配器
NpyString_release_allocator(allocator);
return 0;
fail:
// 失败时释放字符串分配器并返回 -1
NpyString_release_allocator(allocator);
return -1;
}
// 使用字符串长度函数的循环处理函数
static int
string_strlen_strided_loop(PyArrayMethod_Context *context, char *const data[],
npy_intp const dimensions[],
npy_intp const strides[],
NpyAuxData *auxdata)
{
// 获取第一个描述符的字符串数据类型对象
PyArray_StringDTypeObject *descr = (PyArray_StringDTypeObject *)context->descriptors[0];
// 获取字符串分配器
npy_string_allocator *allocator = NpyString_acquire_allocator(descr);
// 检查描述符是否包含字符串 NA
int has_string_na = descr->has_string_na;
// 获取默认字符串
const npy_static_string *default_string = &descr->default_string;
// 获取第一个维度的大小
npy_intp N = dimensions[0];
// 获取输入数据指针
char *in = data[0];
// 获取输出数据指针
char *out = data[1];
// 获取输入步长
npy_intp in_stride = strides[0];
// 获取输出步长
npy_intp out_stride = strides[1];
// 循环,执行N次
while (N--) {
// 将输入的指针解释为np_packed_static_string类型,并赋给ps
const npy_packed_static_string *ps = (npy_packed_static_string *)in;
// 定义并初始化npy_static_string结构体变量s
npy_static_string s = {0, NULL};
// 初始化buffer为空指针,size为0
const char *buffer = NULL;
size_t size = 0;
// 创建一个Buffer对象,模板参数为ENCODING::UTF8
Buffer<ENCODING::UTF8> buf;
// 调用NpyString_load函数加载字符串数据,返回值表示字符串是否为空
int is_null = NpyString_load(allocator, ps, &s);
// 如果加载失败,抛出内存错误,并跳转到fail标签处
if (is_null == -1) {
npy_gil_error(PyExc_MemoryError, "Failed to load string in str_len");
goto fail;
}
// 如果字符串为空
if (is_null) {
// 如果未设置has_string_na标志,抛出数值错误并跳转到next_step标签处
if (!has_string_na) {
npy_gil_error(PyExc_ValueError,
"The length of a null string is undefined");
goto next_step;
}
// 否则使用默认字符串的缓冲区和大小
buffer = default_string->buf;
size = default_string->size;
}
else {
// 否则使用加载的字符串的缓冲区和大小
buffer = s.buf;
size = s.size;
}
// 将buffer和size传递给Buffer对象buf进行初始化
buf = Buffer<ENCODING::UTF8>((char *)buffer, size);
// 将buf的字符数赋给输出指针指向的npy_intp类型变量(假设out是npy_intp*类型)
*(npy_intp *)out = buf.num_codepoints();
next_step:
// 更新输入指针,移动到下一个字符串位置
in += in_stride;
// 更新输出指针,移动到下一个输出位置
out += out_stride;
}
// 释放分配器allocator所分配的内存资源
NpyString_release_allocator(allocator);
// 返回0表示成功执行
return 0;
fail:
NpyString_release_allocator(allocator);
return -1;
}
static int
string_findlike_promoter(PyObject *NPY_UNUSED(ufunc),
PyArray_DTypeMeta *const op_dtypes[],
PyArray_DTypeMeta *const signature[],
PyArray_DTypeMeta *new_op_dtypes[])
{
new_op_dtypes[0] = NPY_DT_NewRef(&PyArray_StringDType);
new_op_dtypes[1] = NPY_DT_NewRef(&PyArray_StringDType);
new_op_dtypes[2] = NPY_DT_NewRef(&PyArray_Int64DType);
new_op_dtypes[3] = NPY_DT_NewRef(&PyArray_Int64DType);
new_op_dtypes[4] = PyArray_DTypeFromTypeNum(NPY_DEFAULT_INT);
return 0;
}
static NPY_CASTING
string_findlike_resolve_descriptors(
struct PyArrayMethodObject_tag *NPY_UNUSED(method),
PyArray_DTypeMeta *const NPY_UNUSED(dtypes[]),
PyArray_Descr *const given_descrs[],
PyArray_Descr *loop_descrs[],
npy_intp *NPY_UNUSED(view_offset))
{
PyArray_StringDTypeObject *descr1 = (PyArray_StringDTypeObject *)given_descrs[0];
PyArray_StringDTypeObject *descr2 = (PyArray_StringDTypeObject *)given_descrs[1];
if (stringdtype_compatible_na(descr1->na_object, descr2->na_object, NULL) == -1) {
return (NPY_CASTING)-1;
}
Py_INCREF(given_descrs[0]);
loop_descrs[0] = given_descrs[0];
Py_INCREF(given_descrs[1]);
loop_descrs[1] = given_descrs[1];
Py_INCREF(given_descrs[2]);
loop_descrs[2] = given_descrs[2];
Py_INCREF(given_descrs[3]);
loop_descrs[3] = given_descrs[3];
if (given_descrs[4] == NULL) {
loop_descrs[4] = PyArray_DescrFromType(NPY_DEFAULT_INT);
}
else {
Py_INCREF(given_descrs[4]);
loop_descrs[4] = given_descrs[4];
}
return NPY_NO_CASTING;
}
static int
string_startswith_endswith_promoter(
PyObject *NPY_UNUSED(ufunc),
PyArray_DTypeMeta *const op_dtypes[],
PyArray_DTypeMeta *const signature[],
PyArray_DTypeMeta *new_op_dtypes[])
{
new_op_dtypes[0] = NPY_DT_NewRef(&PyArray_StringDType);
new_op_dtypes[1] = NPY_DT_NewRef(&PyArray_StringDType);
new_op_dtypes[2] = NPY_DT_NewRef(&PyArray_Int64DType);
new_op_dtypes[3] = NPY_DT_NewRef(&PyArray_Int64DType);
new_op_dtypes[4] = PyArray_DTypeFromTypeNum(NPY_BOOL);
return 0;
}
static NPY_CASTING
string_startswith_endswith_resolve_descriptors(
struct PyArrayMethodObject_tag *NPY_UNUSED(method),
PyArray_DTypeMeta *const NPY_UNUSED(dtypes[]),
PyArray_Descr *const given_descrs[],
PyArray_Descr *loop_descrs[],
npy_intp *NPY_UNUSED(view_offset))
{
PyArray_StringDTypeObject *descr1 = (PyArray_StringDTypeObject *)given_descrs[0];
PyArray_StringDTypeObject *descr2 = (PyArray_StringDTypeObject *)given_descrs[1];
if (stringdtype_compatible_na(descr1->na_object, descr2->na_object, NULL) == -1) {
return (NPY_CASTING)-1;
}
Py_INCREF(given_descrs[0]);
loop_descrs[0] = given_descrs[0];
Py_INCREF(given_descrs[1]);
loop_descrs[1] = given_descrs[1];
Py_INCREF(given_descrs[2]);
loop_descrs[2] = given_descrs[2];
Py_INCREF(given_descrs[3]);
loop_descrs[3] = given_descrs[3];
if (given_descrs[4] == NULL) {
loop_descrs[4] = PyArray_DescrFromType(NPY_BOOL);
}
else {
Py_INCREF(given_descrs[4]);
loop_descrs[4] = given_descrs[4];
}
return NPY_NO_CASTING;
static int
string_findlike_strided_loop(PyArrayMethod_Context *context,
char *const data[],
npy_intp const dimensions[],
npy_intp const strides[],
NpyAuxData *auxdata)
{
// 获取调用该函数的 ufunc 的名字
const char *ufunc_name = ((PyUFuncObject *)context->caller)->name;
// 从静态数据中获取 find_like_function 函数指针
find_like_function *function = *(find_like_function *)(context->method->static_data);
// 获取第一个输入的字符串描述符
PyArray_StringDTypeObject *descr1 = (PyArray_StringDTypeObject *)context->descriptors[0];
// 检查第一个输入是否包含空值
int has_null = descr1->na_object != NULL;
// 检查第一个输入是否包含字符串 NA
int has_string_na = descr1->has_string_na;
// 获取默认字符串
const npy_static_string *default_string = &descr1->default_string;
// 分配字符串的内存分配器
npy_string_allocator *allocators[2] = {};
NpyString_acquire_allocators(2, context->descriptors, allocators);
// 获取第一个和第二个输入的字符串内存分配器
npy_string_allocator *s1allocator = allocators[0];
npy_string_allocator *s2allocator = allocators[1];
// 获取输入数据的指针
char *in1 = data[0]; // 输入字符串 1 的指针
char *in2 = data[1]; // 输入字符串 2 的指针
char *in3 = data[2]; // 输入起始位置的指针
char *in4 = data[3]; // 输入结束位置的指针
char *out = data[4]; // 输出结果的指针
// 获取数据维度的大小
npy_intp N = dimensions[0];
// 迭代处理每一个数据点
while (N--) {
// 加载两个输入字符串
LOAD_TWO_INPUT_STRINGS(ufunc_name);
// 如果输入字符串中有空值
if (NPY_UNLIKELY(s1_isnull || s2_isnull)) {
// 如果支持空值且不支持字符串 NA
if (has_null && !has_string_na) {
// 报告错误并跳转到失败标签
npy_gil_error(PyExc_ValueError,
"'%s' not supported for null values that are not "
"strings.", ufunc_name);
goto fail;
}
else {
// 如果输入字符串 1 是空值,使用默认字符串
if (s1_isnull) {
s1 = *default_string;
}
// 如果输入字符串 2 是空值,使用默认字符串
if (s2_isnull) {
s2 = *default_string;
}
}
}
// 获取起始和结束位置
npy_int64 start = *(npy_int64 *)in3;
npy_int64 end = *(npy_int64 *)in4;
// 将输入字符串转换为 Buffer 对象
Buffer<ENCODING::UTF8> buf1((char *)s1.buf, s1.size);
Buffer<ENCODING::UTF8> buf2((char *)s2.buf, s2.size);
// 调用 find_like_function 函数计算结果
npy_intp pos = function(buf1, buf2, start, end);
// 如果返回特定错误标志,跳转到失败标签
if (pos == -2) {
goto fail;
}
// 将结果写入输出数组
*(npy_intp *)out = pos;
// 更新输入和输出指针位置
in1 += strides[0];
in2 += strides[1];
in3 += strides[2];
in4 += strides[3];
out += strides[4];
}
// 释放字符串的内存分配器
NpyString_release_allocators(2, allocators);
// 返回成功
return 0;
fail:
// 在失败时释放字符串的内存分配器并返回失败
NpyString_release_allocators(2, allocators);
return -1;
}
// 获取第一个描述符,并将其转换为字符串数据类型对象
PyArray_StringDTypeObject *descr1 = (PyArray_StringDTypeObject *)context->descriptors[0];
// 检查是否存在空值
int has_null = descr1->na_object != NULL;
// 检查是否存在字符串类型的空值
int has_string_na = descr1->has_string_na;
// 检查是否存在NaN类型的空值
int has_nan_na = descr1->has_nan_na;
// 获取默认字符串指针
const npy_static_string *default_string = &descr1->default_string;
// 分配两个字符串分配器的空间,并获取描述符中的字符串分配器
npy_string_allocator *allocators[2] = {};
NpyString_acquire_allocators(2, context->descriptors, allocators);
// 分配器1用于第一个描述符
npy_string_allocator *s1allocator = allocators[0];
// 分配器2用于第二个描述符
npy_string_allocator *s2allocator = allocators[1];
// 获取输入数据指针
char *in1 = data[0];
char *in2 = data[1];
char *in3 = data[2];
char *in4 = data[3];
// 获取输出数据指针
char *out = data[4];
// 获取第一维度的大小
npy_intp N = dimensions[0];
// 进入主循环,逐行处理数据
while (N--) {
// 加载两个输入字符串
LOAD_TWO_INPUT_STRINGS(ufunc_name);
// 如果其中一个字符串为null
if (NPY_UNLIKELY(s1_isnull || s2_isnull)) {
// 如果支持null,并且不支持字符串类型的空值
if (has_null && !has_string_na) {
// 如果支持NaN类型的空值
if (has_nan_na) {
// 对于此操作,null始终为假
*(npy_bool *)out = 0;
// 跳转到下一步骤
goto next_step;
}
// 否则,抛出值错误异常
else {
npy_gil_error(PyExc_ValueError,
"'%s' not supported for null values that "
"are not nan-like or strings.", ufunc_name);
// 跳转到失败处理
goto fail;
}
}
// 否则,如果第一个字符串为null,使用默认字符串
else {
if (s1_isnull) {
s1 = *default_string;
}
// 如果第二个字符串为null,使用默认字符串
if (s2_isnull) {
s2 = *default_string;
}
}
}
{
// 从输入数据中获取起始和结束位置
npy_int64 start = *(npy_int64 *)in3;
npy_int64 end = *(npy_int64 *)in4;
// 使用UTF8编码创建缓冲区1和缓冲区2
Buffer<ENCODING::UTF8> buf1((char *)s1.buf, s1.size);
Buffer<ENCODING::UTF8> buf2((char *)s2.buf, s2.size);
// 执行UTF8编码的尾部匹配操作
npy_bool match = tailmatch<ENCODING::UTF8>(buf1, buf2, start, end,
startposition);
// 将匹配结果存储到输出数据中
*(npy_bool *)out = match;
}
next_step:
// 更新输入和输出数据指针
in1 += strides[0];
in2 += strides[1];
in3 += strides[2];
in4 += strides[3];
out += strides[4];
}
// 释放两个字符串分配器
NpyString_release_allocators(2, allocators);
// 返回0表示成功
return 0;
// 释放之前获取的字符串分配器的资源
NpyString_release_allocators(2, allocators);
// 返回错误码 -1
return -1;
}
static int
all_strings_promoter(PyObject *NPY_UNUSED(ufunc),
PyArray_DTypeMeta *const op_dtypes[],
PyArray_DTypeMeta *const signature[],
PyArray_DTypeMeta *new_op_dtypes[])
{
// 将所有新操作数据类型设置为对字符串数据类型的新引用
new_op_dtypes[0] = NPY_DT_NewRef(&PyArray_StringDType);
new_op_dtypes[1] = NPY_DT_NewRef(&PyArray_StringDType);
new_op_dtypes[2] = NPY_DT_NewRef(&PyArray_StringDType);
// 返回成功码 0
return 0;
}
NPY_NO_EXPORT int
string_lrstrip_chars_strided_loop(
PyArrayMethod_Context *context, char *const data[],
npy_intp const dimensions[],
npy_intp const strides[],
NpyAuxData *auxdata)
{
// 获取调用者的名称作为字符串类型
const char *ufunc_name = ((PyUFuncObject *)context->caller)->name;
// 获取方法静态数据中的剥离类型
STRIPTYPE striptype = *(STRIPTYPE *)context->method->static_data;
// 获取第一个描述符作为字符串数据类型对象
PyArray_StringDTypeObject *s1descr = (PyArray_StringDTypeObject *)context->descriptors[0];
// 检查第一个描述符是否有 NULL 对象
int has_null = s1descr->na_object != NULL;
// 检查第一个描述符是否包含字符串 NA 值
int has_string_na = s1descr->has_string_na;
// 检查第一个描述符是否包含 NaN NA 值
int has_nan_na = s1descr->has_nan_na;
// 获取默认字符串的静态指针
const npy_static_string *default_string = &s1descr->default_string;
// 获取数据的第一维度大小
npy_intp N = dimensions[0];
// 获取输入数据数组的指针
char *in1 = data[0];
char *in2 = data[1];
char *out = data[2];
// 定义字符串分配器数组,并获取三个描述符的字符串分配器
npy_string_allocator *allocators[3] = {};
NpyString_acquire_allocators(3, context->descriptors, allocators);
npy_string_allocator *s1allocator = allocators[0];
npy_string_allocator *s2allocator = allocators[1];
npy_string_allocator *oallocator = allocators[2];
while (N--) {
LOAD_TWO_INPUT_STRINGS(ufunc_name);
// 从输入数组中加载两个字符串
npy_packed_static_string *ops = (npy_packed_static_string *)out;
// 将输出指针类型转换为静态字符串结构体指针
if (NPY_UNLIKELY(s1_isnull || s2_isnull)) {
// 如果 s1 或 s2 是空值
if (has_string_na || !has_null) {
// 如果存在字符串类型的空值或者没有空值
if (s1_isnull) {
s1 = *default_string;
// 如果 s1 是空值,则使用默认字符串
}
if (s2_isnull) {
s2 = *default_string;
// 如果 s2 是空值,则使用默认字符串
}
}
else if (has_nan_na) {
// 如果存在 NaN 类型的空值
if (s2_isnull) {
npy_gil_error(PyExc_ValueError,
"Cannot use a null string that is not a "
"string as the %s delimiter", ufunc_name);
// 报错,不能使用非字符串类型的空字符串作为分隔符
}
if (s1_isnull) {
if (NpyString_pack_null(oallocator, ops) < 0) {
npy_gil_error(PyExc_MemoryError,
"Failed to deallocate string in %s",
ufunc_name);
// 打印内存错误信息
goto fail;
}
goto next_step;
// 跳转到下一步
}
}
else {
npy_gil_error(PyExc_ValueError,
"Can only strip null values that are strings "
"or NaN-like values");
// 报错,只能去除字符串类型或类似 NaN 的空值
goto fail;
// 跳转到错误处理
}
}
{
char *new_buf = (char *)PyMem_RawCalloc(s1.size, 1);
// 分配新的内存缓冲区
Buffer<ENCODING::UTF8> buf1((char *)s1.buf, s1.size);
Buffer<ENCODING::UTF8> buf2((char *)s2.buf, s2.size);
Buffer<ENCODING::UTF8> outbuf(new_buf, s1.size);
// 创建 UTF-8 编码的缓冲区对象
size_t new_buf_size = string_lrstrip_chars
(buf1, buf2, outbuf, striptype);
// 调用字符串去除函数,计算新缓冲区的大小
if (NpyString_pack(oallocator, ops, new_buf, new_buf_size) < 0) {
npy_gil_error(PyExc_MemoryError, "Failed to pack string in %s",
ufunc_name);
// 打印内存错误信息
PyMem_RawFree(new_buf);
// 释放内存
goto fail;
// 跳转到错误处理
}
PyMem_RawFree(new_buf);
// 释放内存
}
next_step:
// 下一步标签
in1 += strides[0];
in2 += strides[1];
out += strides[2];
// 更新输入输出指针的位置
}
NpyString_release_allocators(3, allocators);
// 释放分配器
return 0;
fail:
// 调用NpyString_release_allocators函数释放分配的字符串分配器
NpyString_release_allocators(3, allocators);
// 返回-1,表示函数执行失败
return -1;
}
static NPY_CASTING
strip_whitespace_resolve_descriptors(
struct PyArrayMethodObject_tag *NPY_UNUSED(method),
PyArray_DTypeMeta *const NPY_UNUSED(dtypes[]),
PyArray_Descr *const given_descrs[],
PyArray_Descr *loop_descrs[],
npy_intp *NPY_UNUSED(view_offset))
{
// 增加给定描述符的引用计数
Py_INCREF(given_descrs[0]);
// 将给定的描述符赋值给循环描述符数组的第一个元素
loop_descrs[0] = given_descrs[0];
// 初始化输出描述符指针为NULL
PyArray_Descr *out_descr = NULL;
// 如果第二个给定描述符为NULL
if (given_descrs[1] == NULL) {
// 创建一个新的字符串类型实例作为输出描述符
out_descr = (PyArray_Descr *)new_stringdtype_instance(
((PyArray_StringDTypeObject *)given_descrs[0])->na_object,
((PyArray_StringDTypeObject *)given_descrs[0])->coerce);
// 如果无法创建输出描述符实例,返回-1表示失败
if (out_descr == NULL) {
return (NPY_CASTING)-1;
}
}
else {
// 增加第二个给定描述符的引用计数
Py_INCREF(given_descrs[1]);
// 将第二个给定描述符赋值给输出描述符
out_descr = given_descrs[1];
}
// 将输出描述符赋值给循环描述符数组的第二个元素
loop_descrs[1] = out_descr;
// 返回NPY_NO_CASTING,表示无需类型转换
return NPY_NO_CASTING;
}
static int
string_lrstrip_whitespace_strided_loop(
PyArrayMethod_Context *context,
char *const data[], npy_intp const dimensions[],
npy_intp const strides[], NpyAuxData *NPY_UNUSED(auxdata))
{
// 获取调用者的ufunc名称
const char *ufunc_name = ((PyUFuncObject *)context->caller)->name;
// 获取strip类型,这是一个枚举值
STRIPTYPE striptype = *(STRIPTYPE *)context->method->static_data;
// 获取第一个描述符作为字符串类型对象
PyArray_StringDTypeObject *descr = (PyArray_StringDTypeObject *)context->descriptors[0];
// 检查是否存在na_object
int has_null = descr->na_object != NULL;
// 检查是否具有string_na标志
int has_string_na = descr->has_string_na;
// 检查是否具有nan_na标志
int has_nan_na = descr->has_nan_na;
// 获取默认字符串的指针
const npy_static_string *default_string = &descr->default_string;
// 分配两个字符串分配器的数组
npy_string_allocator *allocators[2] = {};
// 获取两个描述符的字符串分配器
NpyString_acquire_allocators(2, context->descriptors, allocators);
// 第一个分配器用于输入数据,第二个分配器用于输出数据
npy_string_allocator *allocator = allocators[0];
npy_string_allocator *oallocator = allocators[1];
// 输入数据的指针
char *in = data[0];
// 输出数据的指针
char *out = data[1];
// 数据的维度大小
npy_intp N = dimensions[0];
// 这里是函数的主要逻辑,后续的代码应该继续注释,但超出了最大字符数限制,因此结束。
// 循环,N 逐步减少直到为 0
while (N--) {
// 将输入的指针视为 npy_packed_static_string 类型的静态字符串指针
const npy_packed_static_string *ps = (npy_packed_static_string *)in;
// 定义并初始化一个静态字符串 s
npy_static_string s = {0, NULL};
// 调用 NpyString_load 函数加载字符串 s,并检查是否为空
int s_isnull = NpyString_load(allocator, ps, &s);
// 如果加载过程中出现错误,抛出内存错误并跳转到失败处理标签
if (s_isnull == -1) {
npy_gil_error(PyExc_MemoryError, "Failed to load string in %s", ufunc_name);
goto fail;
}
// 将输出指针视为 npy_packed_static_string 类型的静态字符串指针
npy_packed_static_string *ops = (npy_packed_static_string *)out;
// 如果 s 为空
if (NPY_UNLIKELY(s_isnull)) {
// 如果允许字符串为空或者不存在空值,使用默认字符串替换 s
if (has_string_na || !has_null) {
s = *default_string;
}
// 如果存在 NaN 类型的空值
else if (has_nan_na) {
// 使用空值填充 ops,并检查是否出错
if (NpyString_pack_null(oallocator, ops) < 0) {
npy_gil_error(PyExc_MemoryError, "Failed to deallocate string in %s", ufunc_name);
goto fail;
}
// 跳转到下一步处理标签
goto next_step;
}
// 其他情况,抛出数值错误,说明只能去除字符串类型的空值或 NaN 类型的空值
else {
npy_gil_error(PyExc_ValueError, "Can only strip null values that are strings or NaN-like values");
goto fail;
}
}
{
// 分配新的缓冲区,用于处理字符串操作
char *new_buf = (char *)PyMem_RawCalloc(s.size, 1);
// 创建输入和输出的 UTF-8 编码缓冲区
Buffer<ENCODING::UTF8> buf((char *)s.buf, s.size);
Buffer<ENCODING::UTF8> outbuf(new_buf, s.size);
// 对字符串进行去除空白字符处理,得到新的缓冲区大小
size_t new_buf_size = string_lrstrip_whitespace(buf, outbuf, striptype);
// 将处理后的字符串打包到 ops,检查是否出错
if (NpyString_pack(oallocator, ops, new_buf, new_buf_size) < 0) {
npy_gil_error(PyExc_MemoryError, "Failed to pack string in %s", ufunc_name);
goto fail;
}
// 释放新分配的缓冲区
PyMem_RawFree(new_buf);
}
next_step:
// 更新输入和输出指针的位置
in += strides[0];
out += strides[1];
}
// 释放所有的分配器资源
NpyString_release_allocators(2, allocators);
// 返回成功状态码
return 0;
fail:
// 释放所有的分配器资源
NpyString_release_allocators(2, allocators);
// 返回失败状态码
return -1;
static int
string_replace_promoter(PyObject *NPY_UNUSED(ufunc),
PyArray_DTypeMeta *const op_dtypes[],
PyArray_DTypeMeta *const signature[],
PyArray_DTypeMeta *new_op_dtypes[])
{
// 设置新的操作数据类型为字符串类型的引用
new_op_dtypes[0] = NPY_DT_NewRef(&PyArray_StringDType);
new_op_dtypes[1] = NPY_DT_NewRef(&PyArray_StringDType);
new_op_dtypes[2] = NPY_DT_NewRef(&PyArray_StringDType);
new_op_dtypes[3] = NPY_DT_NewRef(&PyArray_Int64DType);
new_op_dtypes[4] = NPY_DT_NewRef(&PyArray_StringDType);
// 返回操作成功
return 0;
}
static NPY_CASTING
replace_resolve_descriptors(struct PyArrayMethodObject_tag *NPY_UNUSED(method),
PyArray_DTypeMeta *const NPY_UNUSED(dtypes[]),
PyArray_Descr *const given_descrs[],
PyArray_Descr *loop_descrs[],
npy_intp *NPY_UNUSED(view_offset))
{
// 获取给定描述符的字符串类型对象
PyArray_StringDTypeObject *descr1 = (PyArray_StringDTypeObject *)given_descrs[0];
PyArray_StringDTypeObject *descr2 = (PyArray_StringDTypeObject *)given_descrs[1];
PyArray_StringDTypeObject *descr3 = (PyArray_StringDTypeObject *)given_descrs[2];
// 判断是否需要强制转换
int out_coerce = descr1->coerce && descr2->coerce && descr3->coerce;
PyObject *out_na_object = NULL;
// 检查字符串类型的可兼容性
if (stringdtype_compatible_na(
descr1->na_object, descr2->na_object, &out_na_object) == -1) {
return (NPY_CASTING)-1;
}
// 继续检查另一组字符串类型的兼容性
if (stringdtype_compatible_na(
out_na_object, descr3->na_object, &out_na_object) == -1) {
return (NPY_CASTING)-1;
}
// 增加给定描述符的引用计数,并将其复制到循环描述符中
Py_INCREF(given_descrs[0]);
loop_descrs[0] = given_descrs[0];
Py_INCREF(given_descrs[1]);
loop_descrs[1] = given_descrs[1];
Py_INCREF(given_descrs[2]);
loop_descrs[2] = given_descrs[2];
Py_INCREF(given_descrs[3]);
loop_descrs[3] = given_descrs[3];
PyArray_Descr *out_descr = NULL;
// 如果第四个描述符为空,则创建新的字符串类型实例
if (given_descrs[4] == NULL) {
out_descr = (PyArray_Descr *)new_stringdtype_instance(
out_na_object, out_coerce);
// 如果创建失败,则返回错误状态
if (out_descr == NULL) {
return (NPY_CASTING)-1;
}
}
else {
// 否则增加给定描述符的引用计数,并直接使用
Py_INCREF(given_descrs[4]);
out_descr = given_descrs[4];
}
// 将输出描述符放入循环描述符中
loop_descrs[4] = out_descr;
// 返回不需要强制转换的状态
return NPY_NO_CASTING;
}
static int
string_replace_strided_loop(
PyArrayMethod_Context *context,
char *const data[], npy_intp const dimensions[],
npy_intp const strides[], NpyAuxData *NPY_UNUSED(auxdata))
{
// 获取输入和输出数据的指针
char *in1 = data[0];
char *in2 = data[1];
char *in3 = data[2];
char *in4 = data[3];
char *out = data[4];
// 获取数据的维度大小
npy_intp N = dimensions[0];
// 获取描述符对象并检查其属性
PyArray_StringDTypeObject *descr0 =
(PyArray_StringDTypeObject *)context->descriptors[0];
int has_null = descr0->na_object != NULL;
int has_string_na = descr0->has_string_na;
int has_nan_na = descr0->has_nan_na;
const npy_static_string *default_string = &descr0->default_string;
这些注释详细说明了每行代码的功能和作用,遵循了提供的示例和要求。
npy_string_allocator *allocators[5] = {};
NpyString_acquire_allocators(5, context->descriptors, allocators);
npy_string_allocator *i1allocator = allocators[0];
npy_string_allocator *i2allocator = allocators[1];
npy_string_allocator *i3allocator = allocators[2];
npy_string_allocator *oallocator = allocators[4];
NpyString_release_allocators(5, allocators);
return 0;
fail:
NpyString_release_allocators(5, allocators);
return -1;
}
static NPY_CASTING expandtabs_resolve_descriptors(
struct PyArrayMethodObject_tag *NPY_UNUSED(method),
PyArray_DTypeMeta *const NPY_UNUSED(dtypes[]),
PyArray_Descr *const given_descrs[],
PyArray_Descr *loop_descrs[],
npy_intp *NPY_UNUSED(view_offset))
{
// 增加给定描述符的引用计数,并将其赋给循环描述符数组中的第一个位置
Py_INCREF(given_descrs[0]);
loop_descrs[0] = given_descrs[0];
// 增加给定描述符的引用计数,并将其赋给循环描述符数组中的第二个位置
Py_INCREF(given_descrs[1]);
loop_descrs[1] = given_descrs[1];
// 初始化输出描述符为 NULL
PyArray_Descr *out_descr = NULL;
// 将给定描述符数组中的第一个描述符转换为字符串类型描述符对象
PyArray_StringDTypeObject *idescr =
(PyArray_StringDTypeObject *)given_descrs[0];
// 如果第三个给定描述符为空
if (given_descrs[2] == NULL) {
// 创建新的字符串类型实例作为输出描述符
out_descr = (PyArray_Descr *)new_stringdtype_instance(
idescr->na_object, idescr->coerce);
// 如果创建失败,则返回错误标志
if (out_descr == NULL) {
return (NPY_CASTING)-1;
}
}
else {
// 如果第三个给定描述符不为空,则增加其引用计数并直接赋给输出描述符
Py_INCREF(given_descrs[2]);
out_descr = given_descrs[2];
}
// 将输出描述符赋给循环描述符数组中的第三个位置
loop_descrs[2] = out_descr;
// 返回无需转换的标志
return NPY_NO_CASTING;
}
static int
string_expandtabs_strided_loop(PyArrayMethod_Context *context,
char *const data[],
npy_intp const dimensions[],
npy_intp const strides[],
NpyAuxData *NPY_UNUSED(auxdata))
{
// 获取输入字符串数组的指针
char *in1 = data[0];
char *in2 = data[1];
// 获取输出字符串数组的指针
char *out = data[2];
// 获取数组的长度
npy_intp N = dimensions[0];
// 将描述符数组中的第一个描述符转换为字符串类型描述符对象
PyArray_StringDTypeObject *descr0 =
(PyArray_StringDTypeObject *)context->descriptors[0];
// 检查第一个描述符对象是否包含字符串 NA
int has_string_na = descr0->has_string_na;
// 获取默认字符串
const npy_static_string *default_string = &descr0->default_string;
// 分配字符串操作所需的分配器数组
npy_string_allocator *allocators[3] = {};
NpyString_acquire_allocators(3, context->descriptors, allocators);
npy_string_allocator *iallocator = allocators[0];
// allocators[1] 是 NULL,因为这里没有第二个输入
npy_string_allocator *oallocator = allocators[2];
// 使用 while 循环,循环执行 N 次,每次处理一个字符串
while (N--) {
// 将输入指针强制转换为 npy_packed_static_string 类型,赋给 ips
const npy_packed_static_string *ips = (npy_packed_static_string *)in1;
// 将输出指针强制转换为 npy_packed_static_string 类型,赋给 ops
npy_packed_static_string *ops = (npy_packed_static_string *)out;
// 创建并初始化 npy_static_string 结构体 is
npy_static_string is = {0, NULL};
// 从输入中读取 tabsize,赋给 tabsize
npy_int64 tabsize = *(npy_int64 *)in2;
// 调用 NpyString_load 函数加载字符串 is
int isnull = NpyString_load(iallocator, ips, &is);
// 如果加载失败,抛出内存错误异常,并跳转到 fail 标签处
if (isnull == -1) {
npy_gil_error(
PyExc_MemoryError, "Failed to load string in expandtabs");
goto fail;
}
// 如果加载的字符串为空
else if (isnull) {
// 如果不支持字符串 NA(缺失值),抛出值错误异常,并跳转到 fail 标签处
if (!has_string_na) {
npy_gil_error(PyExc_ValueError,
"Null values are not supported arguments for "
"expandtabs");
goto fail;
}
// 否则,使用默认字符串替代 is
else {
is = *default_string;
}
}
// 使用 Buffer 类型的 buf 封装 is 的数据和大小
Buffer<ENCODING::UTF8> buf((char *)is.buf, is.size);
// 计算扩展制表符后的新字符串大小
npy_intp new_buf_size = string_expandtabs_length(buf, tabsize);
// 如果新字符串大小小于 0,跳转到 fail 标签处
if (new_buf_size < 0) {
goto fail;
}
// 分配 new_buf_size 大小的内存,并初始化为 0,赋给 new_buf
char *new_buf = (char *)PyMem_RawCalloc(new_buf_size, 1);
// 使用 Buffer 类型的 outbuf 封装 new_buf 和 new_buf_size
Buffer<ENCODING::UTF8> outbuf(new_buf, new_buf_size);
// 执行字符串扩展制表符处理
string_expandtabs(buf, tabsize, outbuf);
// 将处理后的字符串打包到 ops 中,如果失败,抛出内存错误异常,并跳转到 fail 标签处
if (NpyString_pack(oallocator, ops, new_buf, new_buf_size) < 0) {
npy_gil_error(
PyExc_MemoryError, "Failed to pack string in expandtabs");
goto fail;
}
// 释放 new_buf 的内存
PyMem_RawFree(new_buf);
// 更新输入和输出指针位置
in1 += strides[0];
in2 += strides[1];
out += strides[2];
}
// 释放所有分配器
NpyString_release_allocators(3, allocators);
// 成功执行,返回 0
return 0;
fail:
// 发生失败,释放所有分配器,并返回 -1
NpyString_release_allocators(3, allocators);
return -1;
}
static NPY_CASTING
center_ljust_rjust_resolve_descriptors(
struct PyArrayMethodObject_tag *NPY_UNUSED(method),
PyArray_DTypeMeta *const dtypes[], PyArray_Descr *const given_descrs[],
PyArray_Descr *loop_descrs[], npy_intp *NPY_UNUSED(view_offset))
{
PyArray_StringDTypeObject *input_descr = (PyArray_StringDTypeObject *)given_descrs[0];
// 获取输入字符串类型描述符
PyArray_StringDTypeObject *fill_descr = (PyArray_StringDTypeObject *)given_descrs[2];
// 获取填充字符串类型描述符
int out_coerce = input_descr->coerce && fill_descr->coerce;
// 检查是否需要强制转换输出
PyObject *out_na_object = NULL;
if (stringdtype_compatible_na(
input_descr->na_object, fill_descr->na_object, &out_na_object) == -1) {
// 检查输入和填充描述符的兼容性,设置输出 NA 对象
return (NPY_CASTING)-1;
// 若兼容性检查失败则返回错误码
}
Py_INCREF(given_descrs[0]);
// 增加输入描述符的引用计数
loop_descrs[0] = given_descrs[0];
// 设置循环描述符数组的第一个元素为输入描述符
Py_INCREF(given_descrs[1]);
// 增加输入描述符的引用计数
loop_descrs[1] = given_descrs[1];
// 设置循环描述符数组的第二个元素为输入描述符
Py_INCREF(given_descrs[2]);
// 增加输入描述符的引用计数
loop_descrs[2] = given_descrs[2];
// 设置循环描述符数组的第三个元素为输入描述符
PyArray_Descr *out_descr = NULL;
if (given_descrs[3] == NULL) {
// 若输出描述符为空
out_descr = (PyArray_Descr *)new_stringdtype_instance(
out_na_object, out_coerce);
// 根据输出 NA 对象和是否需要强制转换创建新的字符串类型实例
if (out_descr == NULL) {
// 若创建实例失败
return (NPY_CASTING)-1;
// 返回错误码
}
}
else {
// 若输出描述符不为空
Py_INCREF(given_descrs[3]);
// 增加输出描述符的引用计数
out_descr = given_descrs[3];
// 设置输出描述符为给定的输出描述符
}
loop_descrs[3] = out_descr;
// 设置循环描述符数组的第四个元素为输出描述符
return NPY_NO_CASTING;
// 返回无需转换的标志
}
static int
center_ljust_rjust_strided_loop(PyArrayMethod_Context *context,
char *const data[],
npy_intp const dimensions[],
npy_intp const strides[],
NpyAuxData *NPY_UNUSED(auxdata))
{
PyArray_StringDTypeObject *s1descr = (PyArray_StringDTypeObject *)context->descriptors[0];
// 获取第一个字符串描述符
int has_null = s1descr->na_object != NULL;
// 检查第一个描述符是否有 NA 对象
int has_nan_na = s1descr->has_nan_na;
// 检查第一个描述符是否有 NaN 或 NA
int has_string_na = s1descr->has_string_na;
// 检查第一个描述符是否有字符串 NA
const npy_static_string *default_string = &s1descr->default_string;
// 获取第一个描述符的默认字符串
npy_intp N = dimensions[0];
// 获取维度的大小
char *in1 = data[0];
// 获取输入数据数组的第一个元素
char *in2 = data[1];
// 获取输入数据数组的第二个元素
char *in3 = data[2];
// 获取输入数据数组的第三个元素
char *out = data[3];
// 获取输出数据数组的第四个元素
npy_intp in1_stride = strides[0];
// 获取输入数据数组的第一个元素的步长
npy_intp in2_stride = strides[1];
// 获取输入数据数组的第二个元素的步长
npy_intp in3_stride = strides[2];
// 获取输入数据数组的第三个元素的步长
npy_intp out_stride = strides[3];
// 获取输出数据数组的第四个元素的步长
npy_string_allocator *allocators[4] = {};
// 创建字符串分配器数组
NpyString_acquire_allocators(4, context->descriptors, allocators);
// 获取字符串分配器
npy_string_allocator *s1allocator = allocators[0];
// 获取第一个字符串分配器
// allocators[1] is NULL
npy_string_allocator *s2allocator = allocators[2];
// 获取第三个字符串分配器
npy_string_allocator *oallocator = allocators[3];
// 获取第四个字符串分配器
JUSTPOSITION pos = *(JUSTPOSITION *)(context->method->static_data);
// 获取 JUSTPOSITION 结构体
const char* ufunc_name = ((PyUFuncObject *)context->caller)->name;
// 获取 UFunc 的名称
}
NpyString_release_allocators(4, allocators);
// 释放字符串分配器
return 0;
fail:
NpyString_release_allocators(4, allocators);
// 释放字符串分配器
return -1;
}
static int
zfill_strided_loop(PyArrayMethod_Context *context,
char *const data[], npy_intp const dimensions[],
npy_intp const strides[], NpyAuxData *NPY_UNUSED(auxdata))
{
PyArray_StringDTypeObject *idescr =
(PyArray_StringDTypeObject *)context->descriptors[0];
npy_intp N = dimensions[0];
char *in1 = data[0];
char *in2 = data[1];
char *out = data[2];
npy_intp in1_stride = strides[0];
npy_intp in2_stride = strides[1];
npy_intp out_stride = strides[2];
npy_string_allocator *allocators[3] = {};
NpyString_acquire_allocators(3, context->descriptors, allocators);
npy_string_allocator *iallocator = allocators[0];
npy_string_allocator *oallocator = allocators[2];
int has_null = idescr->na_object != NULL;
int has_nan_na = idescr->has_nan_na;
int has_string_na = idescr->has_string_na;
const npy_static_string *default_string = &idescr->default_string;
}
NpyString_release_allocators(3, allocators);
return 0;
fail:
NpyString_release_allocators(3, allocators);
return -1;
}
static NPY_CASTING
string_partition_resolve_descriptors(
PyArrayMethodObject *self,
PyArray_DTypeMeta *const NPY_UNUSED(dtypes[3]),
PyArray_Descr *const given_descrs[3],
PyArray_Descr *loop_descrs[3],
npy_intp *NPY_UNUSED(view_offset))
{
if (given_descrs[2] || given_descrs[3] || given_descrs[4]) {
PyErr_Format(PyExc_TypeError, "The StringDType '%s' ufunc does not "
"currently support the 'out' keyword", self->name);
return (NPY_CASTING)-1;
}
PyArray_StringDTypeObject *descr1 = (PyArray_StringDTypeObject *)given_descrs[0];
PyArray_StringDTypeObject *descr2 = (PyArray_StringDTypeObject *)given_descrs[1];
int out_coerce = descr1->coerce && descr2->coerce;
PyObject *out_na_object = NULL;
if (stringdtype_compatible_na(
descr1->na_object, descr2->na_object, &out_na_object) == -1) {
return (NPY_CASTING)-1;
}
Py_INCREF(given_descrs[0]);
loop_descrs[0] = given_descrs[0];
Py_INCREF(given_descrs[1]);
loop_descrs[1] = given_descrs[1];
for (int i=2; i<5; i++) {
loop_descrs[i] = (PyArray_Descr *)new_stringdtype_instance(
out_na_object, out_coerce);
if (loop_descrs[i] == NULL) {
return (NPY_CASTING)-1;
}
}
return NPY_NO_CASTING;
}
NPY_NO_EXPORT int
string_partition_strided_loop(
PyArrayMethod_Context *context,
char *const data[],
npy_intp const dimensions[],
npy_intp const strides[],
NpyAuxData *NPY_UNUSED(auxdata))
{
STARTPOSITION startposition = *(STARTPOSITION *)(context->method->static_data);
int fastsearch_direction =
startposition == STARTPOSITION::FRONT ? FAST_SEARCH : FAST_RSEARCH;
npy_intp N = dimensions[0];
char *in1 = data[0];
char *in2 = data[1];
char *out1 = data[2];
char *out2 = data[3];
char *out3 = data[4];
npy_intp in1_stride = strides[0];
npy_intp in2_stride = strides[1];
npy_intp out1_stride = strides[2];
npy_intp out2_stride = strides[3];
npy_intp out3_stride = strides[4];
npy_string_allocator *allocators[5] = {};
NpyString_acquire_allocators(5, context->descriptors, allocators);
npy_string_allocator *in1allocator = allocators[0];
npy_string_allocator *in2allocator = allocators[1];
npy_string_allocator *out1allocator = allocators[2];
npy_string_allocator *out2allocator = allocators[3];
npy_string_allocator *out3allocator = allocators[4];
PyArray_StringDTypeObject *idescr =
(PyArray_StringDTypeObject *)context->descriptors[0];
int has_string_na = idescr->has_string_na;
const npy_static_string *default_string = &idescr->default_string;
NpyString_release_allocators(5, allocators);
return 0;
fail:
NpyString_release_allocators(5, allocators);
return -1;
}
NPY_NO_EXPORT int
string_inputs_promoter(
PyObject *ufunc_obj, PyArray_DTypeMeta *const op_dtypes[],
PyArray_DTypeMeta *const signature[],
PyArray_DTypeMeta *new_op_dtypes[],
PyArray_DTypeMeta *final_dtype,
PyArray_DTypeMeta *result_dtype)
{
PyUFuncObject *ufunc = (PyUFuncObject *)ufunc_obj;
/* set all input operands to final_dtype */
for (int i = 0; i < ufunc->nin; i++) {
PyArray_DTypeMeta *tmp = final_dtype;
if (signature[i]) {
tmp = signature[i]; /* never replace a fixed one. */
}
Py_INCREF(tmp);
new_op_dtypes[i] = tmp;
}
/* don't touch output dtypes if they are set */
for (int i = ufunc->nin; i < ufunc->nargs; i++) {
if (op_dtypes[i] != NULL) {
Py_INCREF(op_dtypes[i]);
new_op_dtypes[i] = op_dtypes[i];
}
else {
Py_INCREF(result_dtype);
new_op_dtypes[i] = result_dtype;
}
}
return 0;
}
static int
string_object_bool_output_promoter(
PyObject *ufunc, PyArray_DTypeMeta *const op_dtypes[],
PyArray_DTypeMeta *const signature[],
PyArray_DTypeMeta *new_op_dtypes[])
{
return string_inputs_promoter(
ufunc, op_dtypes, signature,
new_op_dtypes, &PyArray_ObjectDType, &PyArray_BoolDType);
}
static int
string_unicode_bool_output_promoter(
PyObject *ufunc, PyArray_DTypeMeta *const op_dtypes[],
PyArray_DTypeMeta *const signature[],
PyArray_DTypeMeta *new_op_dtypes[])
{
return string_inputs_promoter(
ufunc, op_dtypes, signature,
new_op_dtypes, &PyArray_StringDType, &PyArray_BoolDType);
}
static int
is_integer_dtype(PyArray_DTypeMeta *DType)
{
if (DType == &PyArray_PyLongDType) {
return 1;
}
else if (DType == &PyArray_Int8DType) {
return 1;
}
else if (DType == &PyArray_Int16DType) {
return 1;
}
else if (DType == &PyArray_Int32DType) {
return 1;
}
// int64 already has a loop registered for it,
// so don't need to consider it
else if (DType == &PyArray_ByteDType) {
return 1;
}
else if (DType == &PyArray_ShortDType) {
return 1;
}
else if (DType == &PyArray_IntDType) {
return 1;
}
else if (DType == &PyArray_LongLongDType) {
return 1;
}
else if (DType == &PyArray_UInt8DType) {
return 1;
}
else if (DType == &PyArray_UInt16DType) {
return 1;
}
else if (DType == &PyArray_UInt32DType) {
return 1;
}
// uint64 already has a loop registered for it,
// so don't need to consider it
#if NPY_SIZEOF_BYTE == NPY_SIZEOF_SHORT
else if (DType == &PyArray_UByteDType) {
return 1;
}
#endif
// No match found, not an integer dtype
return 0;
}
#ifdef
#if NPY_SIZEOF_SHORT == NPY_SIZEOF_INT
// 如果短整型和整型的字节大小相同,返回1
else if (DType == &PyArray_UShortDType) {
return 1;
}
#endif
#if NPY_SIZEOF_INT == NPY_SIZEOF_LONG
// 如果整型和长整型的字节大小相同,返回1
else if (DType == &PyArray_UIntDType) {
return 1;
}
#endif
#if NPY_SIZEOF_LONGLONG == NPY_SIZEOF_LONG
// 如果长长整型和长整型的字节大小相同,返回1
else if (DType == &PyArray_ULongLongDType) {
return 1;
}
#endif
// 默认情况下返回0
return 0;
}
static int
string_multiply_promoter(PyObject *ufunc_obj,
PyArray_DTypeMeta *const op_dtypes[],
PyArray_DTypeMeta *const signature[],
PyArray_DTypeMeta *new_op_dtypes[])
{
PyUFuncObject *ufunc = (PyUFuncObject *)ufunc_obj;
// 遍历输入参数
for (int i = 0; i < ufunc->nin; i++) {
PyArray_DTypeMeta *tmp = NULL;
// 如果有签名类型,则使用签名类型
if (signature[i]) {
tmp = signature[i];
}
// 如果是整数类型,则使用64位整型
else if (is_integer_dtype(op_dtypes[i])) {
tmp = &PyArray_Int64DType;
}
// 如果存在操作类型,则使用该类型
else if (op_dtypes[i]) {
tmp = op_dtypes[i];
}
// 默认使用字符串类型
else {
tmp = &PyArray_StringDType;
}
// 增加临时类型的引用计数
Py_INCREF(tmp);
new_op_dtypes[i] = tmp;
}
/* don't touch output dtypes if they are set */
// 如果输出类型已经设置,则不修改输出类型
for (int i = ufunc->nin; i < ufunc->nargs; i++) {
if (op_dtypes[i]) {
// 增加输出类型的引用计数
Py_INCREF(op_dtypes[i]);
new_op_dtypes[i] = op_dtypes[i];
}
else {
// 默认使用字符串类型
Py_INCREF(&PyArray_StringDType);
new_op_dtypes[i] = &PyArray_StringDType;
}
}
return 0;
}
// Register a ufunc.
//
// Pass NULL for resolve_func to use the default_resolve_descriptors.
int
init_ufunc(PyObject *umath, const char *ufunc_name, PyArray_DTypeMeta **dtypes,
PyArrayMethod_ResolveDescriptors *resolve_func,
PyArrayMethod_StridedLoop *loop_func, int nin, int nout,
NPY_CASTING casting, NPY_ARRAYMETHOD_FLAGS flags,
void *static_data)
{
PyObject *ufunc = PyObject_GetAttrString(umath, ufunc_name);
if (ufunc == NULL) {
return -1;
}
char loop_name[256] = {0};
// 根据ufunc_name创建循环名称
snprintf(loop_name, sizeof(loop_name), "string_%s", ufunc_name);
// 定义PyArrayMethod_Spec结构体
PyArrayMethod_Spec spec;
spec.name = loop_name;
spec.nin = nin;
spec.nout = nout;
spec.casting = casting;
spec.flags = flags;
spec.dtypes = dtypes;
spec.slots = NULL;
// 设置PyType_Slot resolve_slots数组
PyType_Slot resolve_slots[] = {
{NPY_METH_resolve_descriptors, (void *)resolve_func},
{NPY_METH_strided_loop, (void *)loop_func},
{_NPY_METH_static_data, static_data},
{0, NULL}};
// 将resolve_slots数组赋值给spec.slots
spec.slots = resolve_slots;
// 将循环规范添加到ufunc中
if (PyUFunc_AddLoopFromSpec_int(ufunc, &spec, 1) < 0) {
Py_DECREF(ufunc);
return -1;
}
// 减少ufunc的引用计数
Py_DECREF(ufunc);
return 0;
}
int
add_promoter(PyObject *numpy, const char *ufunc_name,
PyArray_DTypeMeta *dtypes[], size_t n_dtypes,
PyArrayMethod_PromoterFunction *promoter_impl)
{
// 从numpy对象中获取ufunc对象
PyObject *ufunc = PyObject_GetAttrString((PyObject *)numpy, ufunc_name);
if (ufunc == NULL) {
return -1;
}
// 剩余代码未提供,不进行注释
if (ufunc == NULL) {
return -1;
}
PyObject *DType_tuple = PyTuple_New(n_dtypes);
if (DType_tuple == NULL) {
Py_DECREF(ufunc);
return -1;
}
for (size_t i=0; i<n_dtypes; i++) {
Py_INCREF((PyObject *)dtypes[i]);
PyTuple_SET_ITEM(DType_tuple, i, (PyObject *)dtypes[i]);
}
PyObject *promoter_capsule = PyCapsule_New((void *)promoter_impl,
"numpy._ufunc_promoter", NULL);
if (promoter_capsule == NULL) {
Py_DECREF(ufunc);
Py_DECREF(DType_tuple);
return -1;
}
if (PyUFunc_AddPromoter(ufunc, DType_tuple, promoter_capsule) < 0) {
Py_DECREF(promoter_capsule);
Py_DECREF(DType_tuple);
Py_DECREF(ufunc);
return -1;
}
Py_DECREF(promoter_capsule);
Py_DECREF(DType_tuple);
Py_DECREF(ufunc);
return 0;
// 创建一个用于右操作数为指定类型的乘法通用函数类型数组
PyArray_DTypeMeta *multiply_right_
&PyArray_StringDType, &PyArray_
&PyArray_StringDType}; \
\
// 初始化右操作数为指定类型的乘法通用函数
if (init_ufunc(umath, "multiply", multiply_right_
&multiply_resolve_descriptors, \
&multiply_right_strided_loop<npy_
NPY_NO_CASTING, (NPY_ARRAYMETHOD_FLAGS) 0, NULL) < 0) { \
return -1; \
} \
\
// 创建一个用于左操作数为指定类型的乘法通用函数类型数组
PyArray_DTypeMeta *multiply_left_
&PyArray_
&PyArray_StringDType}; \
\
// 初始化左操作数为指定类型的乘法通用函数
if (init_ufunc(umath, "multiply", multiply_left_
&multiply_resolve_descriptors, \
&multiply_left_strided_loop<npy_
NPY_NO_CASTING, (NPY_ARRAYMETHOD_FLAGS) 0, NULL) < 0) { \
return -1; \
}
// 定义一个静态常量字符指针数组,包含6个字符串,表示比较操作的函数名
static const char *comparison_ufunc_names[6] = {
"equal", "not_equal",
"less", "less_equal", "greater_equal", "greater",
};
// 定义一个 PyArray_DTypeMeta 指针数组,包含3个数据类型元信息对象,用于比较操作
PyArray_DTypeMeta *comparison_dtypes[] = {
&PyArray_StringDType,
&PyArray_StringDType, &PyArray_BoolDType};
// 定义一个静态布尔数组,表示在 string_cmp_strided_loop 函数中,eq 和 ne 的比较结果
static npy_bool comparison_ufunc_eq_lt_gt_results[6*3] = {
NPY_TRUE, NPY_FALSE, NPY_FALSE, // eq: results for eq, lt, gt
NPY_FALSE, NPY_TRUE, NPY_TRUE, // ne
NPY_FALSE, NPY_TRUE, NPY_FALSE, // lt
NPY_TRUE, NPY_TRUE, NPY_FALSE, // le
NPY_TRUE, NPY_FALSE, NPY_TRUE, // gt
NPY_FALSE, NPY_FALSE, NPY_TRUE, // ge
};
// 循环初始化比较操作的通用函数
for (int i = 0; i < 6; i++) {
// 初始化通用函数 umath 中的比较函数,如果失败则返回 -1
if (init_ufunc(umath, comparison_ufunc_names[i], comparison_dtypes,
&string_comparison_resolve_descriptors,
&string_comparison_strided_loop, 2, 1, NPY_NO_CASTING,
(NPY_ARRAYMETHOD_FLAGS) 0,
&comparison_ufunc_eq_lt_gt_results[i*3]) < 0) {
return -1;
}
// 将对象和 Unicode 提升器添加到 umath 中的比较函数中,如果失败则返回 -1
if (add_object_and_unicode_promoters(
umath, comparison_ufunc_names[i],
&string_unicode_bool_output_promoter,
&string_object_bool_output_promoter) < 0) {
return -1;
}
}
// 定义一个布尔输出数据类型的 PyArray_DTypeMeta 指针数组
PyArray_DTypeMeta *bool_output_dtypes[] = {
&PyArray_StringDType,
&PyArray_BoolDType
};
// 初始化 umath 中的 "isnan" 函数,如果失败则返回 -1
if (init_ufunc(umath, "isnan", bool_output_dtypes,
&string_bool_output_resolve_descriptors,
&string_isnan_strided_loop, 1, 1, NPY_NO_CASTING,
(NPY_ARRAYMETHOD_FLAGS) 0, NULL) < 0) {
return -1;
}
// 定义一个一元操作的函数名字符串数组
const char *unary_loop_names[] = {
"isalpha", "isdecimal", "isdigit", "isnumeric", "isspace",
"isalnum", "istitle", "isupper", "islower",
};
// 定义一组 utf8_buffer_method 指针数组,指向一元操作函数成员方法
static utf8_buffer_method unary_loop_buffer_methods[] = {
&Buffer<ENCODING::UTF8>::isalpha,
&Buffer<ENCODING::UTF8>::isdecimal,
&Buffer<ENCODING::UTF8>::isdigit,
&Buffer<ENCODING::UTF8>::isnumeric,
&Buffer<ENCODING::UTF8>::isspace,
&Buffer<ENCODING::UTF8>::isalnum,
&Buffer<ENCODING::UTF8>::istitle,
&Buffer<ENCODING::UTF8>::isupper,
&Buffer<ENCODING::UTF8>::islower,
};
// 循环初始化一元操作的通用函数
for (int i=0; i<9; i++) {
// 初始化通用函数 umath 中的一元操作函数,如果失败则返回 -1
if (init_ufunc(umath, unary_loop_names[i], bool_output_dtypes,
&string_bool_output_resolve_descriptors,
&string_bool_output_unary_strided_loop, 1, 1, NPY_NO_CASTING,
(NPY_ARRAYMETHOD_FLAGS) 0,
&unary_loop_buffer_methods[i]) < 0) {
return -1;
}
}
// 定义一个包含两种数据类型元信息的数组,分别为字符串和整数指针
PyArray_DTypeMeta *intp_output_dtypes[] = {
&PyArray_StringDType,
&PyArray_IntpDType
};
// 初始化一个ufunc函数,用于计算字符串长度,设置输出数据类型,解析描述符,并设置循环函数
if (init_ufunc(umath, "str_len", intp_output_dtypes,
&string_intp_output_resolve_descriptors,
&string_strlen_strided_loop, 1, 1, NPY_NO_CASTING,
(NPY_ARRAYMETHOD_FLAGS) 0, NULL) < 0) {
return -1;
}
// 定义一个包含三种字符串数据类型元信息的数组
PyArray_DTypeMeta *binary_dtypes[] = {
&PyArray_StringDType,
&PyArray_StringDType,
&PyArray_StringDType,
};
// 定义包含字符串数组的名称
const char* minimum_maximum_names[] = {"minimum", "maximum"};
// 定义静态布尔数组,用于指示最小和最大的反转状态
static npy_bool minimum_maximum_invert[2] = {NPY_FALSE, NPY_TRUE};
// 循环处理最小和最大名称,为每个名称初始化一个ufunc函数
for (int i = 0; i < 2; i++) {
if (init_ufunc(umath, minimum_maximum_names[i],
binary_dtypes, binary_resolve_descriptors,
&minimum_maximum_strided_loop, 2, 1, NPY_NO_CASTING,
(NPY_ARRAYMETHOD_FLAGS) 0,
&minimum_maximum_invert[i]) < 0) {
return -1;
}
}
// 初始化一个ufunc函数,用于执行加法操作,设置输入输出数据类型和相关处理函数
if (init_ufunc(umath, "add", binary_dtypes, binary_resolve_descriptors,
&add_strided_loop, 2, 1, NPY_NO_CASTING,
(NPY_ARRAYMETHOD_FLAGS) 0, NULL) < 0) {
return -1;
}
// 定义包含三种数据类型元信息的数组,用于所有字符串推广操作
PyArray_DTypeMeta *rall_strings_promoter_dtypes[] = {
&PyArray_StringDType,
&PyArray_UnicodeDType,
&PyArray_StringDType,
};
// 添加所有字符串推广操作的ufunc函数,并指定数据类型和推广函数
if (add_promoter(umath, "add", rall_strings_promoter_dtypes, 3,
all_strings_promoter) < 0) {
return -1;
}
// 定义包含三种数据类型元信息的数组,用于左侧所有字符串推广操作
PyArray_DTypeMeta *lall_strings_promoter_dtypes[] = {
&PyArray_UnicodeDType,
&PyArray_StringDType,
&PyArray_StringDType,
};
// 添加左侧所有字符串推广操作的ufunc函数,并指定数据类型和推广函数
if (add_promoter(umath, "add", lall_strings_promoter_dtypes, 3,
all_strings_promoter) < 0) {
return -1;
}
// 初始化 Int64 和 UInt64 的乘法操作的宏定义
INIT_MULTIPLY(Int64, int64);
INIT_MULTIPLY(UInt64, uint64);
// 定义包含三种数据类型元信息的数组,用于字符串乘法操作
PyArray_DTypeMeta *rdtypes[] = {
&PyArray_StringDType,
&PyArray_IntAbstractDType,
&PyArray_StringDType};
// 添加字符串乘法操作的ufunc函数,并指定数据类型和推广函数
if (add_promoter(umath, "multiply", rdtypes, 3, string_multiply_promoter) < 0) {
return -1;
}
// 定义包含三种数据类型元信息的数组,用于左侧字符串乘法操作
PyArray_DTypeMeta *ldtypes[] = {
&PyArray_IntAbstractDType,
&PyArray_StringDType,
&PyArray_StringDType};
// 添加左侧字符串乘法操作的ufunc函数,并指定数据类型和推广函数
if (add_promoter(umath, "multiply", ldtypes, 3, string_multiply_promoter) < 0) {
return -1;
}
// 定义包含五种数据类型元信息的数组,用于 find-like 操作
PyArray_DTypeMeta *findlike_dtypes[] = {
&PyArray_StringDType, &PyArray_StringDType,
&PyArray_Int64DType, &PyArray_Int64DType,
&PyArray_DefaultIntDType,
};
// 定义包含五种字符串操作名称的数组
const char* findlike_names[] = {
"find", "rfind", "index", "rindex", "count",
};
// 定义包含五种数据类型元信息的数组,用于所有字符串推广操作
PyArray_DTypeMeta *findlike_promoter_dtypes[] = {
&PyArray_StringDType, &PyArray_UnicodeDType,
&PyArray_IntAbstractDType, &PyArray_IntAbstractDType,
&PyArray_DefaultIntDType,
};
// 定义一个函数指针数组,包含查找类函数的指针
find_like_function *findlike_functions[] = {
// 使用UTF-8编码的字符串查找函数
string_find<ENCODING::UTF8>,
// 使用UTF-8编码的字符串反向查找函数
string_rfind<ENCODING::UTF8>,
// 使用UTF-8编码的字符串索引函数
string_index<ENCODING::UTF8>,
// 使用UTF-8编码的字符串反向索引函数
string_rindex<ENCODING::UTF8>,
// 使用UTF-8编码的字符串计数函数
string_count<ENCODING::UTF8>,
};
// 遍历函数指针数组
for (int i=0; i<5; i++) {
// 初始化通用函数,设置查找函数的解析描述符和循环处理函数
if (init_ufunc(umath, findlike_names[i], findlike_dtypes,
&string_findlike_resolve_descriptors,
&string_findlike_strided_loop, 4, 1, NPY_NO_CASTING,
(NPY_ARRAYMETHOD_FLAGS) 0,
(void *)findlike_functions[i]) < 0) {
return -1;
}
// 添加字符串查找类函数的类型提升器
if (add_promoter(umath, findlike_names[i],
findlike_promoter_dtypes,
5, string_findlike_promoter) < 0) {
return -1;
}
}
// 定义开始和结束字符串操作的数据类型数组
PyArray_DTypeMeta *startswith_endswith_dtypes[] = {
&PyArray_StringDType, &PyArray_StringDType,
&PyArray_Int64DType, &PyArray_Int64DType,
&PyArray_BoolDType,
};
// 开始和结束字符串操作的函数名数组
const char* startswith_endswith_names[] = {
"startswith", "endswith",
};
// 开始和结束字符串操作的类型提升器数据类型数组
PyArray_DTypeMeta *startswith_endswith_promoter_dtypes[] = {
&PyArray_StringDType, &PyArray_UnicodeDType,
&PyArray_IntAbstractDType, &PyArray_IntAbstractDType,
&PyArray_BoolDType,
};
// 开始和结束字符串操作的起始位置数组
static STARTPOSITION startswith_endswith_startposition[] = {
STARTPOSITION::FRONT,
STARTPOSITION::BACK,
};
// 遍历开始和结束字符串操作的名称数组
for (int i=0; i<2; i++) {
// 初始化通用函数,设置开始和结束字符串操作的解析描述符和循环处理函数
if (init_ufunc(umath, startswith_endswith_names[i], startswith_endswith_dtypes,
&string_startswith_endswith_resolve_descriptors,
&string_startswith_endswith_strided_loop,
4, 1, NPY_NO_CASTING, (NPY_ARRAYMETHOD_FLAGS) 0,
&startswith_endswith_startposition[i]) < 0) {
return -1;
}
// 添加开始和结束字符串操作的类型提升器
if (add_promoter(umath, startswith_endswith_names[i],
startswith_endswith_promoter_dtypes,
5, string_startswith_endswith_promoter) < 0) {
return -1;
}
}
// 定义去除字符串两端空白字符操作的数据类型数组
PyArray_DTypeMeta *strip_whitespace_dtypes[] = {
&PyArray_StringDType, &PyArray_StringDType
};
// 去除字符串两端空白字符操作的函数名数组
const char *strip_whitespace_names[] = {
"_lstrip_whitespace", "_rstrip_whitespace", "_strip_whitespace",
};
// 去除字符串两端空白字符操作的类型数组
static STRIPTYPE strip_types[] = {
STRIPTYPE::LEFTSTRIP,
STRIPTYPE::RIGHTSTRIP,
STRIPTYPE::BOTHSTRIP,
};
// 遍历去除字符串两端空白字符操作的名称数组
for (int i=0; i<3; i++) {
// 初始化通用函数,设置去除字符串两端空白字符的解析描述符和循环处理函数
if (init_ufunc(umath, strip_whitespace_names[i], strip_whitespace_dtypes,
&strip_whitespace_resolve_descriptors,
&string_lrstrip_whitespace_strided_loop,
1, 1, NPY_NO_CASTING, (NPY_ARRAYMETHOD_FLAGS) 0,
&strip_types[i]) < 0) {
return -1;
}
}
PyArray_DTypeMeta *strip_chars_dtypes[] = {
&PyArray_StringDType, &PyArray_StringDType, &PyArray_StringDType
};
const char *strip_chars_names[] = {
"_lstrip_chars", "_rstrip_chars", "_strip_chars",
};
for (int i=0; i<3; i++) {
if (init_ufunc(umath, strip_chars_names[i], strip_chars_dtypes,
&binary_resolve_descriptors,
&string_lrstrip_chars_strided_loop,
2, 1, NPY_NO_CASTING, (NPY_ARRAYMETHOD_FLAGS) 0,
&strip_types[i]) < 0) {
return -1;
}
if (add_promoter(umath, strip_chars_names[i],
rall_strings_promoter_dtypes, 3,
all_strings_promoter) < 0) {
return -1;
}
if (add_promoter(umath, strip_chars_names[i],
lall_strings_promoter_dtypes, 3,
all_strings_promoter) < 0) {
return -1;
}
}
PyArray_DTypeMeta *replace_dtypes[] = {
&PyArray_StringDType, &PyArray_StringDType, &PyArray_StringDType,
&PyArray_Int64DType, &PyArray_StringDType,
};
if (init_ufunc(umath, "_replace", replace_dtypes,
&replace_resolve_descriptors,
&string_replace_strided_loop, 4, 1,
NPY_NO_CASTING,
(NPY_ARRAYMETHOD_FLAGS) 0, NULL) < 0) {
return -1;
}
PyArray_DTypeMeta *replace_promoter_pyint_dtypes[] = {
&PyArray_StringDType, &PyArray_UnicodeDType, &PyArray_UnicodeDType,
&PyArray_IntAbstractDType, &PyArray_StringDType,
};
if (add_promoter(umath, "_replace", replace_promoter_pyint_dtypes, 5,
string_replace_promoter) < 0) {
return -1;
}
PyArray_DTypeMeta *replace_promoter_int64_dtypes[] = {
&PyArray_StringDType, &PyArray_UnicodeDType, &PyArray_UnicodeDType,
&PyArray_Int64DType, &PyArray_StringDType,
};
if (add_promoter(umath, "_replace", replace_promoter_int64_dtypes, 5,
string_replace_promoter) < 0) {
return -1;
}
PyArray_DTypeMeta *expandtabs_dtypes[] = {
&PyArray_StringDType,
&PyArray_Int64DType,
&PyArray_StringDType,
};
if (init_ufunc(umath, "_expandtabs", expandtabs_dtypes,
&expandtabs_resolve_descriptors,
&string_expandtabs_strided_loop, 2, 1,
NPY_NO_CASTING,
(NPY_ARRAYMETHOD_FLAGS) 0, NULL) < 0) {
return -1;
}
PyArray_DTypeMeta *expandtabs_promoter_dtypes[] = {
&PyArray_StringDType,
(PyArray_DTypeMeta *)Py_None,
&PyArray_StringDType
};
if (add_promoter(umath, "_expandtabs", expandtabs_promoter_dtypes,
3, string_multiply_promoter) < 0) {
return -1;
}
PyArray_DTypeMeta *center_ljust_rjust_dtypes[] = {
&PyArray_StringDType,
&PyArray_Int64DType,
&PyArray_StringDType,
&PyArray_StringDType,
};
static const char* center_ljust_rjust_names[3] = {
"_center", "_ljust", "_rjust"
};
static JUSTPOSITION positions[3] = {
JUSTPOSITION::CENTER, JUSTPOSITION::LEFT, JUSTPOSITION::RIGHT
};
for (int i=0; i<3; i++) {
if (init_ufunc(umath, center_ljust_rjust_names[i],
center_ljust_rjust_dtypes,
¢er_ljust_rjust_resolve_descriptors,
¢er_ljust_rjust_strided_loop, 3, 1, NPY_NO_CASTING,
(NPY_ARRAYMETHOD_FLAGS) 0, &positions[i]) < 0) {
return -1;
}
PyArray_DTypeMeta *int_promoter_dtypes[] = {
&PyArray_StringDType,
(PyArray_DTypeMeta *)Py_None,
&PyArray_StringDType,
&PyArray_StringDType,
};
if (add_promoter(umath, center_ljust_rjust_names[i],
int_promoter_dtypes, 4,
string_multiply_promoter) < 0) {
return -1;
}
PyArray_DTypeMeta *unicode_promoter_dtypes[] = {
&PyArray_StringDType,
(PyArray_DTypeMeta *)Py_None,
&PyArray_UnicodeDType,
&PyArray_StringDType,
};
if (add_promoter(umath, center_ljust_rjust_names[i],
unicode_promoter_dtypes, 4,
string_multiply_promoter) < 0) {
return -1;
}
}
PyArray_DTypeMeta *zfill_dtypes[] = {
&PyArray_StringDType,
&PyArray_Int64DType,
&PyArray_StringDType,
};
if (init_ufunc(umath, "_zfill", zfill_dtypes, multiply_resolve_descriptors,
zfill_strided_loop, 2, 1, NPY_NO_CASTING,
(NPY_ARRAYMETHOD_FLAGS) 0, NULL) < 0) {
return -1;
}
PyArray_DTypeMeta *int_promoter_dtypes[] = {
&PyArray_StringDType,
(PyArray_DTypeMeta *)Py_None,
&PyArray_StringDType,
};
if (add_promoter(umath, "_zfill", int_promoter_dtypes, 3,
string_multiply_promoter) < 0) {
return -1;
}
PyArray_DTypeMeta *partition_dtypes[] = {
&PyArray_StringDType,
&PyArray_StringDType,
&PyArray_StringDType,
&PyArray_StringDType,
&PyArray_StringDType
};
const char *partition_names[] = {"_partition", "_rpartition"};
static STARTPOSITION partition_startpositions[] = {
STARTPOSITION::FRONT, STARTPOSITION::BACK
};
for (int i=0; i<2; i++) {
if (init_ufunc(umath, partition_names[i], partition_dtypes,
string_partition_resolve_descriptors,
string_partition_strided_loop, 2, 3, NPY_NO_CASTING,
(NPY_ARRAYMETHOD_FLAGS) 0, &partition_startpositions[i]) < 0) {
return -1;
}
}
return 0;
}
注释:
.\numpy\numpy\_core\src\umath\stringdtype_ufuncs.h
extern "C" {
// 声明一个不导出的函数,用于初始化字符串数据类型的通用函数
NPY_NO_EXPORT int
init_stringdtype_ufuncs(PyObject* umath);
}
.\numpy\numpy\_core\src\umath\string_buffer.h
// 定义不使用过时 API 的标志
// 定义多维数组模块
// 定义数学模块
// 定义宏,检查索引是否溢出
// 定义宏,提取一个整数的最高有效位
// 枚举类型,表示字符编码方式
enum class ENCODING {
ASCII, UTF32, UTF8
};
// 枚举类型,表示实现的一元函数
enum class IMPLEMENTED_UNARY_FUNCTIONS {
ISALPHA,
ISDECIMAL,
ISDIGIT,
ISSPACE,
ISALNUM,
ISLOWER,
ISUPPER,
ISTITLE,
ISNUMERIC,
STR_LEN,
};
// 模板函数,根据编码类型返回字符
template <ENCODING enc>
inline npy_ucs4
getchar(const unsigned char *buf, int *bytes);
// 特化模板函数,处理 ASCII 编码类型的字符
template <>
inline npy_ucs4
getchar<ENCODING::ASCII>(const unsigned char *buf, int *bytes)
{
*bytes = 1; // ASCII 字符占用一个字节
return (npy_ucs4) *buf; // 返回字符的 Unicode 码点
}
// 特化模板函数,处理 UTF32 编码类型的字符
template <>
inline npy_ucs4
getchar<ENCODING::UTF32>(const unsigned char *buf, int *bytes)
{
*bytes = 4; // UTF32 字符占用四个字节
return *(npy_ucs4 *)buf; // 返回 UTF32 编码字符的 Unicode 码点
}
// 特化模板函数,处理 UTF8 编码类型的字符
template <>
inline npy_ucs4
getchar<ENCODING::UTF8>(const unsigned char *buf, int *bytes)
{
Py_UCS4 codepoint;
*bytes = utf8_char_to_ucs4_code(buf, &codepoint); // 将 UTF8 字符转换为 Unicode 码点
return (npy_ucs4)codepoint; // 返回 Unicode 码点
}
// 模板函数,根据编码类型判断字符是否为字母
template<ENCODING enc>
inline bool
codepoint_isalpha(npy_ucs4 code);
// 特化模板函数,处理 ASCII 编码类型的字母判断
template<>
inline bool
codepoint_isalpha<ENCODING::ASCII>(npy_ucs4 code)
{
return NumPyOS_ascii_isalpha(code); // 使用 NumPy 的 ASCII 字母判断函数
}
// 特化模板函数,处理 UTF32 编码类型的字母判断
template<>
inline bool
codepoint_isalpha<ENCODING::UTF32>(npy_ucs4 code)
{
return Py_UNICODE_ISALPHA(code); // 使用 Python 内部的 UTF32 字母判断函数
}
// 特化模板函数,处理 UTF8 编码类型的字母判断
template<>
inline bool
codepoint_isalpha<ENCODING::UTF8>(npy_ucs4 code)
{
return Py_UNICODE_ISALPHA(code); // 使用 Python 内部的 UTF8 字母判断函数
}
// 模板函数,根据编码类型判断字符是否为数字
template<ENCODING enc>
inline bool
codepoint_isdigit(npy_ucs4 code);
// 特化模板函数,处理 ASCII 编码类型的数字判断
template<>
inline bool
codepoint_isdigit<ENCODING::ASCII>(npy_ucs4 code)
{
return NumPyOS_ascii_isdigit(code); // 使用 NumPy 的 ASCII 数字判断函数
}
// 特化模板函数,处理 UTF32 编码类型的数字判断
template<>
inline bool
codepoint_isdigit<ENCODING::UTF32>(npy_ucs4 code)
{
return Py_UNICODE_ISDIGIT(code); // 使用 Python 内部的 UTF32 数字判断函数
}
// 特化模板函数,处理 UTF8 编码类型的数字判断
template<>
inline bool
codepoint_isdigit<ENCODING::UTF8>(npy_ucs4 code)
{
return Py_UNICODE_ISDIGIT(code); // 使用 Python 内部的 UTF8 数字判断函数
}
// 模板函数,根据编码类型判断字符是否为空白字符
template<ENCODING enc>
inline bool
codepoint_isspace(npy_ucs4 code);
// 特化模板函数,处理 ASCII 编码类型的空白字符判断
template<>
inline bool
codepoint_isspace<ENCODING::ASCII>(npy_ucs4 code)
{
return NumPyOS_ascii_isspace(code); // 使用 NumPy 的 ASCII 空白字符判断函数
}
// 特化模板函数,处理 UTF32 编码类型的空白字符判断
template<>
inline bool
codepoint_isspace<ENCODING::UTF32>(npy_ucs4 code)
{
return Py_UNICODE_ISSPACE(code); // 使用 Python 内部的 UTF32 空白字符判断函数
}
// 特化模板函数,处理 UTF8 编码类型的空白字符判断
template<>
inline bool
codepoint_isspace<ENCODING::UTF8>(npy_ucs4 code)
{
return Py_UNICODE_ISSPACE(code); // 使用 Python 内部的 UTF8 空白字符判断函数
}
// 模板函数,根据编码类型判断字符是否为字母或数字
template<ENCODING enc>
inline bool
codepoint_isalnum(npy_ucs4 code);
// 特化模板函数,处理 ASCII 编码类型的字母或数字判断
template<>
inline bool
codepoint_isalnum<ENCODING::ASCII>(npy_ucs4 code)
{
return NumPyOS_ascii_isalnum(code); // 使用 NumPy 的 ASCII 字母或数字判断函数
}
// 特化模板函数,处理 UTF32 编码类型的字母或数字判断
template<>
inline bool
codepoint_isalnum<ENCODING::UTF32>(npy_ucs4 code)
{
return Py_UNICODE_ISALNUM(code); // 使用 Python 内部的 UTF32 字母或数字判断函数
}
// 特化模板函数,处理 UTF8 编码类型的字母或数字判断
template<>
inline bool
codepoint_isalnum<ENCODING::UTF8>(npy_ucs4 code)
{
return Py_UNICODE_ISALNUM(code); // 使用 Python 内部的 UTF8 字母或数字判断函数
}
// 继续添加更多的模板函数实现...
// 声明模板特化,用于判断给定编码下的 Unicode 代码点是否为小写字母
template<>
inline bool
codepoint_islower<ENCODING::ASCII>(npy_ucs4 code)
{
return NumPyOS_ascii_islower(code);
}
// 声明模板特化,用于判断给定编码下的 Unicode 代码点是否为小写字母
template<>
inline bool
codepoint_islower<ENCODING::UTF32>(npy_ucs4 code)
{
return Py_UNICODE_ISLOWER(code);
}
// 声明模板特化,用于判断给定编码下的 Unicode 代码点是否为小写字母
template<>
inline bool
codepoint_islower<ENCODING::UTF8>(npy_ucs4 code)
{
return Py_UNICODE_ISLOWER(code);
}
// 声明模板特化,用于判断给定编码下的 Unicode 代码点是否为大写字母
template<ENCODING enc>
inline bool
codepoint_isupper(npy_ucs4 code);
// 模板特化实现,用于判断 ASCII 编码下的 Unicode 代码点是否为大写字母
template<>
inline bool
codepoint_isupper<ENCODING::ASCII>(npy_ucs4 code)
{
return NumPyOS_ascii_isupper(code);
}
// 模板特化实现,用于判断 UTF32 编码下的 Unicode 代码点是否为大写字母
template<>
inline bool
codepoint_isupper<ENCODING::UTF32>(npy_ucs4 code)
{
return Py_UNICODE_ISUPPER(code);
}
// 模板特化实现,用于判断 UTF8 编码下的 Unicode 代码点是否为大写字母
template<>
inline bool
codepoint_isupper<ENCODING::UTF8>(npy_ucs4 code)
{
return Py_UNICODE_ISUPPER(code);
}
// 声明模板特化,用于判断给定编码下的 Unicode 代码点是否为标题字母
template<ENCODING enc>
inline bool
codepoint_istitle(npy_ucs4);
// 模板特化实现,ASCII 编码下的 Unicode 代码点不支持标题字母的定义,始终返回 false
template<>
inline bool
codepoint_istitle<ENCODING::ASCII>(npy_ucs4 code)
{
return false;
}
// 模板特化实现,UTF32 编码下的 Unicode 代码点是否为标题字母
template<>
inline bool
codepoint_istitle<ENCODING::UTF32>(npy_ucs4 code)
{
return Py_UNICODE_ISTITLE(code);
}
// 模板特化实现,UTF8 编码下的 Unicode 代码点是否为标题字母
template<>
inline bool
codepoint_istitle<ENCODING::UTF8>(npy_ucs4 code)
{
return Py_UNICODE_ISTITLE(code);
}
// 判断给定的 Unicode 代码点是否为数字
inline bool
codepoint_isnumeric(npy_ucs4 code)
{
return Py_UNICODE_ISNUMERIC(code);
}
// 判断给定的 Unicode 代码点是否为十进制数字
inline bool
codepoint_isdecimal(npy_ucs4 code)
{
return Py_UNICODE_ISDECIMAL(code);
}
{
// 开始一个 switch 结构,根据 enc 的值进行不同的操作
switch (enc) {
case ENCODING::ASCII:
// 如果编码是 ASCII,将 buf 减去 rhs
buf -= rhs;
break;
case ENCODING::UTF32:
// 如果编码是 UTF32,将 buf 减去 rhs 乘以每个字符的字节数(sizeof(npy_ucs4))
buf -= rhs * sizeof(npy_ucs4);
break;
case ENCODING::UTF8:
// 如果编码是 UTF8,查找 buf 中前一个 UTF8 字符的位置,并将 buf 设置为该位置
buf = (char *) find_previous_utf8_character((unsigned char *)buf, (size_t) rhs);
}
// 返回当前对象的引用
return *this;
}
inline Buffer<enc>&
operator++()
{
// 前缀递增运算符重载,调用后缀递增运算符重载,然后返回当前对象的引用
*this += 1;
return *this;
}
inline Buffer<enc>
operator++(int)
{
// 后缀递增运算符重载,保存当前对象的副本,调用前缀递增运算符重载,然后返回保存的副本
Buffer<enc> old = *this;
operator++();
return old;
}
inline Buffer<enc>&
operator--()
{
// 前缀递减运算符重载,调用后缀递减运算符重载,然后返回当前对象的引用
*this -= 1;
return *this;
}
inline Buffer<enc>
operator--(int)
{
// 后缀递减运算符重载,保存当前对象的副本,调用前缀递减运算符重载,然后返回保存的副本
Buffer<enc> old = *this;
operator--();
return old;
}
inline npy_ucs4
operator*()
{
// 解引用运算符重载,获取当前位置 buf 的字符并返回
int bytes;
return getchar<enc>((unsigned char *) buf, &bytes);
}
inline int
buffer_memcmp(Buffer<enc> other, size_t len)
{
// 比较当前对象 buf 和另一个对象 other 的前 len 字节或字符
if (len == 0) {
return 0;
}
switch (enc) {
case ENCODING::ASCII:
case ENCODING::UTF8:
// 对于 ASCII 和 UTF8,按字节比较 len 长度
return memcmp(buf, other.buf, len);
case ENCODING::UTF32:
// 对于 UTF32,按每个字符占用的字节数进行比较
return memcmp(buf, other.buf, len * sizeof(npy_ucs4));
}
}
inline void
buffer_memcpy(Buffer<enc> out, size_t n_chars)
{
// 将当前对象 buf 的前 n_chars 个字节或字符复制到目标对象 out 的 buf 中
if (n_chars == 0) {
return;
}
switch (enc) {
case ENCODING::ASCII:
case ENCODING::UTF8:
// 对于 ASCII 和 UTF8,按字节复制 n_chars 长度
memcpy(out.buf, buf, n_chars);
break;
case ENCODING::UTF32:
// 对于 UTF32,按每个字符占用的字节数进行复制
memcpy(out.buf, buf, n_chars * sizeof(npy_ucs4));
break;
}
}
inline npy_intp
buffer_memset(npy_ucs4 fill_char, size_t n_chars)
{
// 将当前对象 buf 的前 n_chars 个字节或字符设置为 fill_char 所代表的值
if (n_chars == 0) {
return 0;
}
switch (enc) {
case ENCODING::ASCII:
// 对于 ASCII,使用 memset 将 buf 的每个字节设置为 fill_char
memset(this->buf, fill_char, n_chars);
return n_chars;
case ENCODING::UTF32:
{
// 对于 UTF32,将 buf 的每个字符设置为 fill_char,每个字符占用 sizeof(npy_ucs4) 字节
char *tmp = this->buf;
for (size_t i = 0; i < n_chars; i++) {
*(npy_ucs4 *)tmp = fill_char;
tmp += sizeof(npy_ucs4);
}
return n_chars;
}
case ENCODING::UTF8:
{
// 对于 UTF8,将 fill_char 转换为 UTF8 编码后,复制 n_chars 个字符到 buf 中
char utf8_c[4] = {0};
char *tmp = this->buf;
size_t num_bytes = ucs4_code_to_utf8_char(fill_char, utf8_c);
for (size_t i = 0; i < n_chars; i++) {
memcpy(tmp, utf8_c, num_bytes);
tmp += num_bytes;
}
return num_bytes * n_chars;
}
}
}
inline void
buffer_fill_with_zeros_after_index(size_t start_index)
{
// 将从 start_index 开始之后的缓冲区填充为零
Buffer<enc> offset = *this + start_index;
for (char *tmp = offset.buf; tmp < after; tmp++) {
*tmp = 0;
}
}
inline void
advance_chars_or_bytes(size_t n) {
// 根据编码类型前进 n 个字符或字节
switch (enc) {
case ENCODING::ASCII:
case ENCODING::UTF32:
*this += n;
break;
case ENCODING::UTF8:
this->buf += n;
break;
}
}
inline size_t
num_bytes_next_character(void) {
// 返回下一个字符所占的字节数
switch (enc) {
case ENCODING::ASCII:
return 1;
case ENCODING::UTF32:
return 4;
case ENCODING::UTF8:
return num_bytes_for_utf8_character((unsigned char *)(*this).buf);
}
}
template<IMPLEMENTED_UNARY_FUNCTIONS f>
inline bool
unary_loop()
{
// 对每个代码点执行特定的一元操作,直到结束或出现失败
size_t len = num_codepoints();
if (len == 0) {
return false;
}
Buffer<enc> tmp = *this;
for (size_t i=0; i<len; i++) {
bool result;
call_buffer_member_function<f, enc, bool> cbmf;
// 调用特定的一元操作函数
result = cbmf(tmp);
if (!result) {
return false;
}
tmp++;
}
return true;
}
inline bool
isalpha()
{
// 检查是否所有字符都是字母
return unary_loop<IMPLEMENTED_UNARY_FUNCTIONS::ISALPHA>();
}
inline bool
first_character_isspace()
{
// 检查首字符是否为空白字符
switch (enc) {
case ENCODING::ASCII:
return NumPyOS_ascii_isspace(**this);
case ENCODING::UTF32:
case ENCODING::UTF8:
return Py_UNICODE_ISSPACE(**this);
}
}
inline bool
isspace()
{
// 检查是否所有字符都是空白字符
return unary_loop<IMPLEMENTED_UNARY_FUNCTIONS::ISSPACE>();
}
inline bool
isdigit()
{
// 检查是否所有字符都是数字
return unary_loop<IMPLEMENTED_UNARY_FUNCTIONS::ISDIGIT>();
}
inline bool
isalnum()
{
// 检查是否所有字符都是字母或数字
return unary_loop<IMPLEMENTED_UNARY_FUNCTIONS::ISALNUM>();
}
inline bool
islower()
{
// 检查是否所有字符都是小写字母
size_t len = num_codepoints();
if (len == 0) {
return false;
}
Buffer<enc> tmp = *this;
bool cased = 0;
for (size_t i = 0; i < len; i++) {
if (codepoint_isupper<enc>(*tmp) || codepoint_istitle<enc>(*tmp)) {
return false;
}
else if (!cased && codepoint_islower<enc>(*tmp)) {
cased = true;
}
tmp++;
}
return cased;
}
inline bool
isupper()
{
// 获取此缓冲区中的字符数
size_t len = num_codepoints();
// 如果字符数为零,返回 false
if (len == 0) {
return false;
}
// 创建缓冲区的临时副本
Buffer<enc> tmp = *this;
// 指示是否至少有一个字符为大写
bool cased = 0;
// 遍历缓冲区中的每个字符
for (size_t i = 0; i < len; i++) {
// 如果字符是小写或标题格式,则返回 false
if (codepoint_islower<enc>(*tmp) || codepoint_istitle<enc>(*tmp)) {
return false;
}
// 如果之前未遇到大写字符且当前字符是大写,则设置 cased 为 true
else if (!cased && codepoint_isupper<enc>(*tmp)) {
cased = true;
}
// 移动临时副本到下一个字符位置
tmp++;
}
// 返回是否存在大写字符的标志
return cased;
}
inline bool
istitle()
{
// 获取此缓冲区中的字符数
size_t len = num_codepoints();
// 如果字符数为零,返回 false
if (len == 0) {
return false;
}
// 创建缓冲区的临时副本
Buffer<enc> tmp = *this;
// 指示是否存在标题格式的字符
bool cased = false;
// 指示前一个字符是否为标题格式或大写
bool previous_is_cased = false;
// 遍历缓冲区中的每个字符
for (size_t i = 0; i < len; i++) {
// 如果字符是大写或标题格式
if (codepoint_isupper<enc>(*tmp) || codepoint_istitle<enc>(*tmp)) {
// 如果之前已经有字符是标题格式或大写,则返回 false
if (previous_is_cased) {
return false;
}
// 设置前一个字符为标题格式或大写
previous_is_cased = true;
// 设置 cased 为 true
cased = true;
}
// 如果字符是小写
else if (codepoint_islower<enc>(*tmp)) {
// 如果之前没有遇到标题格式或大写字符,则返回 false
if (!previous_is_cased) {
return false;
}
// 设置 cased 为 true
cased = true;
}
// 如果字符既不是大写也不是小写
else {
// 设置前一个字符不是标题格式或大写
previous_is_cased = false;
}
// 移动临时副本到下一个字符位置
tmp++;
}
// 返回是否存在标题格式的字符的标志
return cased;
}
inline bool
isnumeric()
{
// 调用 unary_loop 函数检查是否存在数值字符
return unary_loop<IMPLEMENTED_UNARY_FUNCTIONS::ISNUMERIC>();
}
inline bool
isdecimal()
{
// 调用 unary_loop 函数检查是否存在十进制字符
return unary_loop<IMPLEMENTED_UNARY_FUNCTIONS::ISDECIMAL>();
}
inline Buffer<enc>
rstrip()
{
// 创建缓冲区的临时副本
Buffer<enc> tmp(after, 0);
// 将 tmp 移动到末尾字符的前一个位置
tmp--;
// 移除尾部的空白字符或者 '\0'
while (tmp >= *this && (*tmp == '\0' || NumPyOS_ascii_isspace(*tmp))) {
tmp--;
}
// 将 after 指向 tmp 的缓冲区
after = tmp.buf;
// 返回原始缓冲区
return *this;
}
inline int
strcmp(Buffer<enc> other, bool rstrip)
{
// 如果 rstrip 为 true,则对当前对象和 other 对象执行 rstrip 操作
Buffer tmp1 = rstrip ? this->rstrip() : *this;
Buffer tmp2 = rstrip ? other.rstrip() : other;
// 比较两个缓冲区中的字符内容
while (tmp1.buf < tmp1.after && tmp2.buf < tmp2.after) {
// 如果 tmp1 中的字符小于 tmp2 中的字符,返回 -1
if (*tmp1 < *tmp2) {
return -1;
}
// 如果 tmp1 中的字符大于 tmp2 中的字符,返回 1
if (*tmp1 > *tmp2) {
return 1;
}
// 移动 tmp1 和 tmp2 到下一个字符位置
tmp1++;
tmp2++;
}
// 如果 tmp1 还有剩余字符且不为 '\0',返回 1
while (tmp1.buf < tmp1.after) {
if (*tmp1) {
return 1;
}
tmp1++;
}
// 如果 tmp2 还有剩余字符且不为 '\0',返回 -1
while (tmp2.buf < tmp2.after) {
if (*tmp2) {
return -1;
}
tmp2++;
}
// 如果两个缓冲区内容相同,返回 0
return 0;
}
inline int
strcmp(Buffer<enc> other)
{
// 调用带 rstrip 参数为 false 的 strcmp 函数
return strcmp(other, false);
}
// 结构模板,用于调用缓冲区成员函数的特定操作
template <IMPLEMENTED_UNARY_FUNCTIONS f, ENCODING enc, typename T>
struct call_buffer_member_function {
// 重载函数调用操作符,根据给定的操作选择并调用对应的代码点检查函数
T operator()(Buffer<enc> buf) {
switch (f) {
case IMPLEMENTED_UNARY_FUNCTIONS::ISALPHA:
return codepoint_isalpha<enc>(*buf);
case IMPLEMENTED_UNARY_FUNCTIONS::ISDIGIT:
return codepoint_isdigit<enc>(*buf);
case IMPLEMENTED_UNARY_FUNCTIONS::ISSPACE:
return codepoint_isspace<enc>(*buf);
case IMPLEMENTED_UNARY_FUNCTIONS::ISALNUM:
return codepoint_isalnum<enc>(*buf);
case IMPLEMENTED_UNARY_FUNCTIONS::ISNUMERIC:
return codepoint_isnumeric(*buf);
case IMPLEMENTED_UNARY_FUNCTIONS::ISDECIMAL:
return codepoint_isdecimal(*buf);
}
}
};
// 重载加法操作符,用于将缓冲区和一个整数相加
template <ENCODING enc>
inline Buffer<enc>
operator+(Buffer<enc> lhs, npy_int64 rhs)
{
switch (enc) {
case ENCODING::ASCII:
// 返回一个新的缓冲区,偏移 rhs 个字节后
return Buffer<enc>(lhs.buf + rhs, lhs.after - lhs.buf - rhs);
case ENCODING::UTF32:
// 返回一个新的 UTF32 编码的缓冲区,偏移 rhs 个字符后
return Buffer<enc>(lhs.buf + rhs * sizeof(npy_ucs4),
lhs.after - lhs.buf - rhs * sizeof(npy_ucs4));
case ENCODING::UTF8:
char* buf = lhs.buf;
// 迭代偏移 rhs 次,找到新位置的 UTF8 字符的起始位置
for (int i=0; i<rhs; i++) {
buf += num_bytes_for_utf8_character((unsigned char *)buf);
}
// 返回一个新的 UTF8 编码的缓冲区,偏移后的长度
return Buffer<enc>(buf, (npy_int64)(lhs.after - buf));
}
}
// 重载减法操作符,用于将缓冲区和一个整数相减
template <ENCODING enc>
inline std::ptrdiff_t
operator-(Buffer<enc> lhs, Buffer<enc> rhs)
{
switch (enc) {
case ENCODING::ASCII:
case ENCODING::UTF8:
// 返回两个缓冲区指针之间的偏移量
// 对于 UTF8 字符串,除非比较的是同一字符串中的两个位置,否则结果可能无意义
return lhs.buf - rhs.buf;
case ENCODING::UTF32:
// 返回两个 UTF32 编码的缓冲区指针之间的偏移量,以字符数表示
return (lhs.buf - rhs.buf) / (std::ptrdiff_t) sizeof(npy_ucs4);
}
}
// 重载减法操作符,用于将缓冲区和一个整数相减
template <ENCODING enc>
inline Buffer<enc>
operator-(Buffer<enc> lhs, npy_int64 rhs)
{
switch (enc) {
case ENCODING::ASCII:
// 返回一个新的缓冲区,偏移 rhs 个字节前
return Buffer<enc>(lhs.buf - rhs, lhs.after - lhs.buf + rhs);
case ENCODING::UTF32:
// 返回一个新的 UTF32 编码的缓冲区,偏移 rhs 个字符前
return Buffer<enc>(lhs.buf - rhs * sizeof(npy_ucs4),
lhs.after - lhs.buf + rhs * sizeof(npy_ucs4));
case ENCODING::UTF8:
char* buf = lhs.buf;
// 找到从当前位置往前偏移 rhs 个字符后的 UTF8 字符的起始位置
buf = (char *)find_previous_utf8_character((unsigned char *)buf, rhs);
// 返回一个新的 UTF8 编码的缓冲区,偏移后的长度
return Buffer<enc>(buf, (npy_int64)(lhs.after - buf));
}
}
// 重载相等比较操作符,用于比较两个缓冲区是否相等
template <ENCODING enc>
inline bool
operator==(Buffer<enc> lhs, Buffer<enc> rhs)
{
return lhs.buf == rhs.buf;
}
// 重载不等比较操作符,用于比较两个缓冲区是否不相等
template <ENCODING enc>
inline bool
operator!=(Buffer<enc> lhs, Buffer<enc> rhs)
{
return !(rhs == lhs);
}
// 重载小于比较操作符,用于比较两个缓冲区的大小关系
template <ENCODING enc>
inline bool
operator<(Buffer<enc> lhs, Buffer<enc> rhs)
{
return lhs.buf < rhs.buf;
}
// 重载大于比较操作符,用于比较两个缓冲区的大小关系
template <ENCODING enc>
inline bool
operator>(Buffer<enc> lhs, Buffer<enc> rhs)
{
return rhs < lhs;
}
// 未完成的模板函数声明
template <ENCODING enc>
inline bool
/*
* Operator <= for Buffer objects.
*
* This function provides the less than or equal to comparison
* for Buffer objects. It delegates to the negation of the
* greater than operator for the implementation.
*/
operator<=(Buffer<enc> lhs, Buffer<enc> rhs)
{
return !(lhs > rhs);
}
/*
* Operator >= for Buffer objects.
*
* This function provides the greater than or equal to comparison
* for Buffer objects. It delegates to the negation of the
* less than operator for the implementation.
*/
template <ENCODING enc>
inline bool
operator>=(Buffer<enc> lhs, Buffer<enc> rhs)
{
return !(lhs < rhs);
}
/*
* Helper function to adjust start/end slice values.
*
* This function adjusts start and end slice indices to ensure
* they are within valid bounds for a given length. It modifies
* the start and end values accordingly based on Python's slicing
* rules to handle negative indices and bounds exceeding length.
*/
static inline void
adjust_offsets(npy_int64 *start, npy_int64 *end, size_t len)
{
if (*end > static_cast<npy_int64>(len)) {
*end = len;
}
else if (*end < 0) {
*end += len;
if (*end < 0) {
*end = 0;
}
}
if (*start < 0) {
*start += len;
if (*start < 0) {
*start = 0;
}
}
}
/*
* Find function for strings in a given encoding.
*
* This function searches for buf2 within buf1 using start and end
* indices. It handles different encodings (UTF8, ASCII, UTF32) and
* adjusts the search indices using adjust_offsets() for compatibility
* with Python's str functions. It returns the index of the first occurrence
* of buf2 in buf1[start:end], or -1 if not found.
*/
template <ENCODING enc>
static inline npy_intp
string_find(Buffer<enc> buf1, Buffer<enc> buf2, npy_int64 start, npy_int64 end)
{
size_t len1 = buf1.num_codepoints();
size_t len2 = buf2.num_codepoints();
// Adjust start and end indices to fit within the length of buf1
adjust_offsets(&start, &end, len1);
// If buf2 is longer than the slice [start:end], return -1
if (end - start < static_cast<npy_int64>(len2)) {
return (npy_intp) -1;
}
// If buf2 is empty, return the start index
if (len2 == 0) {
return (npy_intp) start;
}
char *start_loc = NULL;
char *end_loc = NULL;
// Depending on encoding, set start_loc and end_loc appropriately
if (enc == ENCODING::UTF8) {
find_start_end_locs(buf1.buf, (buf1.after - buf1.buf), start, end,
&start_loc, &end_loc);
}
else {
start_loc = (buf1 + start).buf;
end_loc = (buf1 + end).buf;
}
// Handle the case of searching for a single character
if (len2 == 1) {
npy_intp result;
switch (enc) {
case ENCODING::UTF8:
{
// Handle UTF8 characters that span multiple bytes
if (num_bytes_for_utf8_character((const unsigned char *)buf2.buf) > 1) {
goto multibyte_search;
}
// fall through to the ASCII case because this is a one-byte character
}
case ENCODING::ASCII:
{
char ch = *buf2;
CheckedIndexer<char> ind(start_loc, end_loc - start_loc);
result = (npy_intp) findchar(ind, end_loc - start_loc, ch);
// Adjust result index for UTF8 encoding if necessary
if (enc == ENCODING::UTF8 && result > 0) {
result = utf8_character_index(
start_loc, start_loc - buf1.buf, start, result,
buf1.after - start_loc);
}
break;
}
case ENCODING::UTF32:
{
npy_ucs4 ch = *buf2;
CheckedIndexer<npy_ucs4> ind((npy_ucs4 *)(buf1 + start).buf, end-start);
result = (npy_intp) findchar(ind, end - start, ch);
break;
}
}
// Return -1 if character not found, otherwise return adjusted index
if (result == -1) {
return (npy_intp) -1;
}
else {
return result + (npy_intp) start;
}
}
multibyte_search:
npy_intp pos;
// 根据不同的编码格式进行搜索
switch(enc) {
case ENCODING::UTF8:
// 如果是UTF-8编码,使用快速搜索算法在buf2中查找子串位置
pos = fastsearch(start_loc, end_loc - start_loc, buf2.buf, buf2.after - buf2.buf, -1, FAST_SEARCH);
// pos 是字节索引,需要转换为字符索引
if (pos > 0) {
pos = utf8_character_index(start_loc, start_loc - buf1.buf, start, pos, buf1.after - start_loc);
}
break;
case ENCODING::ASCII:
// 如果是ASCII编码,使用快速搜索算法在buf2中查找子串位置
pos = fastsearch(start_loc, end - start, buf2.buf, len2, -1, FAST_SEARCH);
break;
case ENCODING::UTF32:
// 如果是UTF-32编码,使用快速搜索算法在buf2中查找子串位置(类型转换为npy_ucs4*)
pos = fastsearch((npy_ucs4 *)start_loc, end - start,
(npy_ucs4 *)buf2.buf, len2, -1, FAST_SEARCH);
break;
}
// 如果找到了匹配的位置,转换为全局索引
if (pos >= 0) {
pos += start;
}
// 返回搜索到的位置
return pos;
/*
定义了一个模板函数 string_index,用于在 buf1 中查找 buf2 的子串,
并返回其在 buf1 中的索引位置。如果未找到子串,则抛出异常并返回 -2。
*/
template <ENCODING enc>
static inline npy_intp
string_index(Buffer<enc> buf1, Buffer<enc> buf2, npy_int64 start, npy_int64 end)
{
// 调用 string_find 函数在 buf1 中查找 buf2 的位置
npy_intp pos = string_find(buf1, buf2, start, end);
// 如果找不到子串,则抛出 ValueError 异常并返回 -2
if (pos == -1) {
npy_gil_error(PyExc_ValueError, "substring not found");
return -2;
}
// 返回找到的子串在 buf1 中的索引位置
return pos;
}
/*
定义了一个模板函数 string_rfind,用于在 buf1 中逆向查找 buf2 的子串,
并返回其在 buf1 中的索引位置。如果未找到子串或条件不符合,则返回 -1。
*/
template <ENCODING enc>
static inline npy_intp
string_rfind(Buffer<enc> buf1, Buffer<enc> buf2, npy_int64 start, npy_int64 end)
{
// 获取 buf1 和 buf2 的字符数
size_t len1 = buf1.num_codepoints();
size_t len2 = buf2.num_codepoints();
// 调整查找范围的起始和结束位置,确保不超出 buf1 的长度范围
adjust_offsets(&start, &end, len1);
// 如果查找范围内的长度小于 buf2 的长度,则直接返回 -1
if (end - start < static_cast<npy_int64>(len2)) {
return (npy_intp) -1;
}
// 如果 buf2 的长度为 0,则返回查找的结束位置 end
if (len2 == 0) {
return (npy_intp) end;
}
// 初始化起始和结束位置的指针
char *start_loc = NULL;
char *end_loc = NULL;
// 根据编码类型选择不同的查找函数和位置计算方式
if (enc == ENCODING::UTF8) {
// 如果是 UTF-8 编码,则调用 find_start_end_locs 函数获取起始和结束位置的指针
find_start_end_locs(buf1.buf, (buf1.after - buf1.buf), start, end,
&start_loc, &end_loc);
}
else {
// 其他编码方式则直接计算起始和结束位置的指针
start_loc = (buf1 + start).buf;
end_loc = (buf1 + end).buf;
}
// 如果 buf2 的长度为 1,则根据不同的编码类型选择不同的查找方式
if (len2 == 1) {
npy_intp result;
switch (enc) {
case ENCODING::UTF8:
{
// 如果是 UTF-8 编码并且 buf2 是多字节字符,则跳转到 multibyte_search 分支
if (num_bytes_for_utf8_character((const unsigned char *)buf2.buf) > 1) {
goto multibyte_search;
}
// 单字节字符则继续 ASCII 查找
}
// ASCII 编码的情况
case ENCODING::ASCII:
{
// 获取 buf2 的第一个字符
char ch = *buf2;
// 初始化 CheckedIndexer 对象,用于安全地访问字符数组
CheckedIndexer<char> ind(start_loc, end_loc - start_loc);
// 在 [start_loc, end_loc) 范围内逆向查找字符 ch,并返回索引位置
result = (npy_intp) rfindchar(ind, end_loc - start_loc, ch);
// 如果是 UTF-8 编码并且找到了字符,则调整结果索引为字符的实际位置
if (enc == ENCODING::UTF8 && result > 0) {
result = utf8_character_index(
start_loc, start_loc - buf1.buf, start, result,
buf1.after - start_loc);
}
break;
}
// UTF-32 编码的情况
case ENCODING::UTF32:
{
// 获取 buf2 的第一个字符
npy_ucs4 ch = *buf2;
// 初始化 CheckedIndexer 对象,用于安全地访问 UCS-4 编码字符数组
CheckedIndexer<npy_ucs4> ind((npy_ucs4 *)(buf1 + start).buf, end - start);
// 在 [start, end) 范围内逆向查找字符 ch,并返回索引位置
result = (npy_intp) rfindchar(ind, end - start, ch);
break;
}
}
// 如果未找到字符,则返回 -1;否则返回字符的实际位置加上起始位置 start
if (result == -1) {
return (npy_intp) -1;
}
else {
return result + (npy_intp) start;
}
}
multibyte_search:
npy_intp pos;
switch (enc) {
case ENCODING::UTF8:
// 在 buf2 中快速搜索 start_loc 到 end_loc 之间的内容
pos = fastsearch(start_loc, end_loc - start_loc, buf2.buf, buf2.after - buf2.buf, -1, FAST_RSEARCH);
// pos 是字节索引,需要转换为字符索引
if (pos > 0) {
// 根据 UTF-8 编码,计算字符索引
pos = utf8_character_index(start_loc, start_loc - buf1.buf, start, pos, buf1.after - start_loc);
}
break;
case ENCODING::ASCII:
// 在 buf2 中快速搜索 start 到 end 之间的内容
pos = (npy_intp) fastsearch(start_loc, end - start, buf2.buf, len2, -1, FAST_RSEARCH);
break;
case ENCODING::UTF32:
// 在 buf2 中快速搜索 start 到 end 之间的 UTF-32 编码内容
pos = (npy_intp) fastsearch((npy_ucs4 *)start_loc, end - start,
(npy_ucs4 *)buf2.buf, len2, -1, FAST_RSEARCH);
break;
}
// 如果找到位置 pos,则将其转换为全局字符索引
if (pos >= 0) {
pos += start;
}
// 返回最终的位置索引
return pos;
/*
* string_rindex: Searches for the last occurrence of buf2 in buf1 within the range [start, end).
* Returns the index of the occurrence or -2 if not found (with an exception raised).
*/
template <ENCODING enc>
static inline npy_intp
string_rindex(Buffer<enc> buf1, Buffer<enc> buf2, npy_int64 start, npy_int64 end)
{
// Find the last occurrence of buf2 in buf1
npy_intp pos = string_rfind(buf1, buf2, start, end);
// Handle case where substring is not found
if (pos == -1) {
npy_gil_error(PyExc_ValueError, "substring not found");
return -2;
}
// Return the index of the last occurrence
return pos;
}
/*
* string_count: Counts occurrences of buf2 in buf1 within the range [start, end).
* Returns the count of occurrences.
*/
template <ENCODING enc>
static inline npy_intp
string_count(Buffer<enc> buf1, Buffer<enc> buf2, npy_int64 start, npy_int64 end)
{
size_t len1 = buf1.num_codepoints();
size_t len2 = buf2.num_codepoints();
// Adjust start and end positions based on buffer lengths
adjust_offsets(&start, &end, len1);
// If the search range is invalid or empty, return 0 occurrences
if (end < start || end - start < static_cast<npy_int64>(len2)) {
return (npy_intp) 0;
}
// Handle case where buf2 is empty
if (len2 == 0) {
return (end - start) < PY_SSIZE_T_MAX ? end - start + 1 : PY_SSIZE_T_MAX;
}
char *start_loc = NULL;
char *end_loc = NULL;
// Determine start and end positions based on encoding type
if (enc == ENCODING::UTF8) {
find_start_end_locs(buf1.buf, (buf1.after - buf1.buf), start, end,
&start_loc, &end_loc);
}
else {
start_loc = (buf1 + start).buf;
end_loc = (buf1 + end).buf;
}
npy_intp count;
// Perform fast search based on encoding type
switch (enc) {
case ENCODING::UTF8:
count = fastsearch(start_loc, end_loc - start_loc, buf2.buf,
buf2.after - buf2.buf, PY_SSIZE_T_MAX,
FAST_COUNT);
break;
case ENCODING::ASCII:
count = (npy_intp) fastsearch(start_loc, end - start, buf2.buf, len2,
PY_SSIZE_T_MAX, FAST_COUNT);
break;
case ENCODING::UTF32:
count = (npy_intp) fastsearch((npy_ucs4 *)start_loc, end - start,
(npy_ucs4 *)buf2.buf, len2,
PY_SSIZE_T_MAX, FAST_COUNT);
break;
}
// Return 0 if an error occurred during search
if (count < 0) {
return 0;
}
// Return the count of occurrences found
return count;
}
/*
* tailmatch: Checks if buf2 matches the end of buf1 within the specified range [start, end).
* Returns true (1) if there's a match, otherwise false (0).
*/
template <ENCODING enc>
inline npy_bool
tailmatch(Buffer<enc> buf1, Buffer<enc> buf2, npy_int64 start, npy_int64 end,
STARTPOSITION direction)
{
size_t len1 = buf1.num_codepoints();
size_t len2 = buf2.num_codepoints();
// Adjust start and end positions based on buffer lengths
adjust_offsets(&start, &end, len1);
// Calculate the position to start checking for tail match
end -= len2;
if (end < start) {
return 0;
}
// Handle case where buf2 is empty, always return true
if (len2 == 0) {
return 1;
}
size_t offset;
size_t end_sub = len2 - 1;
// Determine offset based on direction of search (front or back)
if (direction == STARTPOSITION::BACK) {
offset = end;
}
else {
offset = start;
}
size_t size2 = len2;
// Adjust size2 for UTF8 encoding
if (enc == ENCODING::UTF8) {
size2 = (buf2.after - buf2.buf);
}
// Create buffer slices for comparison
Buffer start_buf = (buf1 + offset);
Buffer end_buf = start_buf + end_sub;
// Check if the buffers match at the start and end positions
if (*start_buf == *buf2 && *end_buf == *(buf2 + end_sub)) {
return !start_buf.buffer_memcmp(buf2, size2);
}
// Default return indicating no match
// Return false indicating no match
return 0;
}
return 0;
}
// 枚举类型,表示字符串修剪的类型,可以是左修剪、右修剪或者两者结合
enum class STRIPTYPE {
LEFTSTRIP, RIGHTSTRIP, BOTHSTRIP
};
// 模板函数,用于去除字符串两端的空白字符
template <ENCODING enc>
static inline size_t
string_lrstrip_whitespace(Buffer<enc> buf, Buffer<enc> out, STRIPTYPE striptype)
{
// 获取字符串的字符数
size_t len = buf.num_codepoints();
// 如果字符串长度为0
if (len == 0) {
// 如果编码不是UTF-8,则将输出缓冲区填充为零
if (enc != ENCODING::UTF8) {
out.buffer_fill_with_zeros_after_index(0);
}
return 0; // 返回修剪后的长度
}
size_t i = 0;
// 计算输入缓冲区的字节数
size_t num_bytes = (buf.after - buf.buf);
Buffer traverse_buf = Buffer<enc>(buf.buf, num_bytes);
// 如果不是右修剪类型,则从左端开始移除空白字符
if (striptype != STRIPTYPE::RIGHTSTRIP) {
while (i < len) {
if (!traverse_buf.first_character_isspace()) {
break;
}
num_bytes -= traverse_buf.num_bytes_next_character();
traverse_buf++;
i++;
}
}
npy_intp j = len - 1; // j表示从右端开始的索引,如果整个字符串都被修剪,则可能变成负数
if (enc == ENCODING::UTF8) {
traverse_buf = Buffer<enc>(buf.after, 0) - 1; // UTF-8编码时,从尾部开始处理
}
else {
traverse_buf = buf + j; // 其他编码,从末尾字符开始处理
}
// 如果不是左修剪类型,则从右端开始移除空白字符
if (striptype != STRIPTYPE::LEFTSTRIP) {
while (j >= static_cast<npy_intp>(i)) {
if (*traverse_buf != 0 && !traverse_buf.first_character_isspace()) {
break;
}
num_bytes -= traverse_buf.num_bytes_next_character();
traverse_buf--;
j--;
}
}
Buffer offset_buf = buf + i; // 根据i偏移得到修剪后的起始位置
if (enc == ENCODING::UTF8) {
offset_buf.buffer_memcpy(out, num_bytes); // 将修剪后的字符串复制到输出缓冲区
return num_bytes; // 返回修剪后的长度
}
offset_buf.buffer_memcpy(out, j - i + 1); // 将修剪后的字符串复制到输出缓冲区
out.buffer_fill_with_zeros_after_index(j - i + 1); // 将剩余部分填充为零
return j - i + 1; // 返回修剪后的长度
}
// 模板函数,用于根据指定的字符集合修剪字符串的两端字符
template <ENCODING enc>
static inline size_t
string_lrstrip_chars(Buffer<enc> buf1, Buffer<enc> buf2, Buffer<enc> out, STRIPTYPE striptype)
{
// 获取第一个缓冲区的字符数
size_t len1 = buf1.num_codepoints();
// 如果第一个缓冲区长度为0
if (len1 == 0) {
// 如果编码是UTF-8,则将整个第一个缓冲区复制到输出缓冲区
if (enc == ENCODING::UTF8) {
buf1.buffer_memcpy(out, (buf1.after - buf1.buf));
return buf1.after - buf1.buf;
}
// 否则,将第一个缓冲区的长度复制到输出缓冲区,并填充剩余部分为零
buf1.buffer_memcpy(out, len1);
out.buffer_fill_with_zeros_after_index(len1);
return len1;
}
size_t i = 0;
// 获取第一个缓冲区的字节数
size_t num_bytes = (buf1.after - buf1.buf);
Buffer traverse_buf = Buffer<enc>(buf1.buf, num_bytes);
// 确定字符串开始修剪的位置
if (striptype != STRIPTYPE::RIGHTSTRIP) {
while (i < len1) {
// 如果第一个缓冲区的第一个字符不是指定的字符集合中的空白字符,则停止修剪
if (!traverse_buf.first_character_isspace()) {
break;
}
num_bytes -= traverse_buf.num_bytes_next_character();
traverse_buf++;
i++;
}
}
// 获取第二个缓冲区的字符数
size_t len2 = buf2.num_codepoints();
// 如果第二个缓冲区长度为0
if (len2 == 0) {
// 如果编码是UTF-8,则将整个第一个缓冲区复制到输出缓冲区
if (enc == ENCODING::UTF8) {
buf1.buffer_memcpy(out, (buf1.after - buf1.buf));
return buf1.after - buf1.buf;
}
// 否则,将第一个缓冲区的长度复制到输出缓冲区,并填充剩余部分为零
buf1.buffer_memcpy(out, len1);
out.buffer_fill_with_zeros_after_index(len1);
return len1;
}
Buffer offset_buf = buf1 + i; // 根据i偏移得到修剪后的起始位置
if (enc == ENCODING::UTF8) {
offset_buf.buffer_memcpy(out, num_bytes); // 将修剪后的字符串复制到输出缓冲区
return num_bytes; // 返回修剪后的长度
}
offset_buf.buffer_memcpy(out, len1); // 将修剪后的字符串复制到输出缓冲区
out.buffer_fill_with_zeros_after_index(len1); // 将剩余部分填充为零
return len1; // 返回修剪后的长度
}
# 如果不是右侧去除空格类型,则执行以下代码块
if (striptype != STRIPTYPE::RIGHTSTRIP) {
# 循环直到 i 小于 len1
while (i < len1) {
# 声明一个 Py_ssize_t 类型的变量 res
Py_ssize_t res;
# 根据编码类型进行不同处理
switch (enc) {
# 对于 ASCII 和 UTF8 编码
case ENCODING::ASCII:
case ENCODING::UTF8:
{
# 使用 CheckedIndexer<char> 对象 ind,索引 buf2.buf 的前 len2 个元素
CheckedIndexer<char> ind(buf2.buf, len2);
# 调用 findchar<char> 函数,在 ind 中查找 traverse_buf 指向的字符
res = findchar<char>(ind, len2, *traverse_buf);
break;
}
# 对于 UTF32 编码
case ENCODING::UTF32:
{
# 使用 CheckedIndexer<npy_ucs4> 对象 ind,索引 buf2.buf 的前 len2 个元素
CheckedIndexer<npy_ucs4> ind((npy_ucs4 *)buf2.buf, len2);
# 调用 findchar<npy_ucs4> 函数,在 ind 中查找 traverse_buf 指向的字符
res = findchar<npy_ucs4>(ind, len2, *traverse_buf);
break;
}
}
# 如果未找到目标字符,则跳出循环
if (res < 0) {
break;
}
# 更新 num_bytes,减去 traverse_buf 下一个字符的字节数
num_bytes -= traverse_buf.num_bytes_next_character();
# traverse_buf 向后移动一个位置
traverse_buf++;
# i 自增
i++;
}
}
# 初始化 j 为 len1 减 1
npy_intp j = len1 - 1;
# 如果编码类型是 UTF8
if (enc == ENCODING::UTF8) {
# 将 traverse_buf 指向 Buffer<enc>(buf1.after, 0) 的前一个位置
traverse_buf = Buffer<enc>(buf1.after, 0) - 1;
}
# 否则
else {
# 将 traverse_buf 指向 buf1 的末尾
traverse_buf = buf1 + j;
}
# 如果不是左侧去除空格类型,则执行以下代码块
if (striptype != STRIPTYPE::LEFTSTRIP) {
# 循环直到 j 大于等于 i
while (j >= static_cast<npy_intp>(i)) {
# 声明一个 Py_ssize_t 类型的变量 res
Py_ssize_t res;
# 根据编码类型进行不同处理
switch (enc) {
# 对于 ASCII 和 UTF8 编码
case ENCODING::ASCII:
case ENCODING::UTF8:
{
# 使用 CheckedIndexer<char> 对象 ind,索引 buf2.buf 的前 len2 个元素
CheckedIndexer<char> ind(buf2.buf, len2);
# 调用 findchar<char> 函数,在 ind 中查找 traverse_buf 指向的字符
res = findchar<char>(ind, len2, *traverse_buf);
break;
}
# 对于 UTF32 编码
case ENCODING::UTF32:
{
# 使用 CheckedIndexer<npy_ucs4> 对象 ind,索引 buf2.buf 的前 len2 个元素
CheckedIndexer<npy_ucs4> ind((npy_ucs4 *)buf2.buf, len2);
# 调用 findchar<npy_ucs4> 函数,在 ind 中查找 traverse_buf 指向的字符
res = findchar<npy_ucs4>(ind, len2, *traverse_buf);
break;
}
}
# 如果未找到目标字符,则跳出循环
if (res < 0) {
break;
}
# 更新 num_bytes,减去 traverse_buf 下一个字符的字节数
num_bytes -= traverse_buf.num_bytes_next_character();
# j 自减
j--;
# 如果 j 大于 0,则 traverse_buf 向前移动一个位置
if (j > 0) {
traverse_buf--;
}
}
}
# 将 offset_buf 初始化为 buf1 + i
Buffer offset_buf = buf1 + i;
# 如果编码类型是 UTF8
if (enc == ENCODING::UTF8) {
# 将 offset_buf 的数据拷贝到 out 中,并返回 num_bytes
offset_buf.buffer_memcpy(out, num_bytes);
return num_bytes;
}
# 否则
offset_buf.buffer_memcpy(out, j - i + 1);
# 将 out 中索引为 j - i + 1 之后的位置填充为零
out.buffer_fill_with_zeros_after_index(j - i + 1);
# 返回 j - i + 1
return j - i + 1;
// 如果要替换的字符串为空,则直接返回索引 0
template <typename char_type>
static inline npy_intp
findslice_for_replace(CheckedIndexer<char_type> buf1, npy_intp len1,
CheckedIndexer<char_type> buf2, npy_intp len2)
{
if (len2 == 0) {
return 0;
}
// 如果要替换的字符串长度为 1,则在 buf1 中查找该字符的位置并返回索引
if (len2 == 1) {
return (npy_intp) findchar(buf1, len1, *buf2);
}
// 使用快速搜索算法在 buf1 中查找 buf2,并返回第一个匹配的位置索引
return (npy_intp) fastsearch(buf1.buffer, len1, buf2.buffer, len2, -1, FAST_SEARCH);
}
// 根据不同的编码方式进行字符串替换操作
template <ENCODING enc>
static inline size_t
string_replace(Buffer<enc> buf1, Buffer<enc> buf2, Buffer<enc> buf3, npy_int64 count,
Buffer<enc> out)
{
size_t len1 = buf1.num_codepoints(); // 获取 buf1 的字符数
size_t len2 = buf2.num_codepoints(); // 获取 buf2 的字符数
size_t len3 = buf3.num_codepoints(); // 获取 buf3 的字符数
char *start;
size_t length = len1;
// 根据编码类型确定起始位置 start
if (enc == ENCODING::UTF8) {
start = buf1.after; // UTF8 编码的起始位置为 buf1 的末尾后面
length = 0; // 针对 UTF8 编码,长度设为 0
}
else if (enc == ENCODING::UTF32) {
start = buf1.buf + sizeof(npy_ucs4) * len1; // UTF32 编码的起始位置为 buf1 末尾加上 len1 个 UCS4 字节长度
}
else {
start = buf1.buf + len1; // 其他编码的起始位置为 buf1 末尾
}
Buffer<enc> end1(start, length); // 根据起始位置和长度创建 Buffer 对象 end1
size_t span2, span3;
switch(enc) {
case ENCODING::ASCII:
case ENCODING::UTF32:
{
span2 = len2; // 对于 ASCII 和 UTF32 编码,span2 设为 len2
span3 = len3; // 对于 ASCII 和 UTF32 编码,span3 设为 len3
break;
}
case ENCODING::UTF8:
{
span2 = buf2.after - buf2.buf; // 对于 UTF8 编码,span2 是 buf2 的长度
span3 = buf3.after - buf3.buf; // 对于 UTF8 编码,span3 是 buf3 的长度
break;
}
}
size_t ret = 0;
// 只有在替换次数 count 大于 0 且满足一些条件时才尝试进行替换操作
if (count <= 0 // 没有需要替换的内容
|| len1 < len2 // 输入太小无法匹配
|| (len2 <= 0 && len3 <= 0) // 匹配和替换字符串均为空
|| (len2 == len3 && buf2.strcmp(buf3) == 0)) { // 匹配和替换字符串相同
goto copy_rest; // 跳转到 copy_rest 标签处,执行剩余复制操作
}
// 如果需要替换的字符串长度大于 0,则执行以下逻辑
if (len2 > 0) {
// 循环处理每个时间戳
for (npy_int64 time = 0; time < count; time++) {
npy_intp pos; // 定义位置变量
// 根据编码类型选择合适的索引器
switch (enc) {
case ENCODING::ASCII:
case ENCODING::UTF8:
{
// 使用 CheckedIndexer 处理 buf1 和 buf2,获取替换位置
CheckedIndexer<char> ind1(buf1.buf, end1 - buf1);
CheckedIndexer<char> ind2(buf2.buf, span2);
pos = findslice_for_replace(ind1, end1 - buf1, ind2, span2);
break;
}
case ENCODING::UTF32:
{
// 使用 CheckedIndexer 处理 buf1 和 buf2,获取替换位置
CheckedIndexer<npy_ucs4> ind1((npy_ucs4 *)buf1.buf, end1 - buf1);
CheckedIndexer<npy_ucs4> ind2((npy_ucs4 *)buf2.buf, span2);
pos = findslice_for_replace(ind1, end1 - buf1, ind2, span2);
break;
}
}
// 如果未找到替换位置,跳出循环
if (pos < 0) {
break;
}
// 将 buf1 的数据拷贝到 out,更新相关变量
buf1.buffer_memcpy(out, pos);
ret += pos;
out.advance_chars_or_bytes(pos);
buf1.advance_chars_or_bytes(pos);
// 将 buf3 的数据拷贝到 out,更新相关变量
buf3.buffer_memcpy(out, span3);
ret += span3;
out.advance_chars_or_bytes(span3);
buf1.advance_chars_or_bytes(span2);
}
}
else { // 如果匹配字符串为空,则执行交替处理
// 循环处理每个时间戳
while (count > 0) {
// 将 buf3 的数据拷贝到 out,更新相关变量
buf3.buffer_memcpy(out, span3);
ret += span3;
out.advance_chars_or_bytes(span3);
// 减少计数,如果小于等于 0,则跳出循环
if (--count <= 0) {
break;
}
// 根据编码类型选择处理方式
switch (enc) {
case ENCODING::ASCII:
case ENCODING::UTF32:
// 将 buf1 的数据拷贝到 out,更新相关变量
buf1.buffer_memcpy(out, 1);
ret += 1;
break;
case ENCODING::UTF8:
// 获取 UTF-8 字符的字节数量,并将 buf1 的数据拷贝到 out,更新相关变量
size_t n_bytes = buf1.num_bytes_next_character();
buf1.buffer_memcpy(out, n_bytes);
ret += n_bytes;
break;
}
// 更新 buf1 和 out 的位置
buf1 += 1;
out += 1;
}
}
copy_rest:
// 使用 buf1 的 buffer_memcpy 方法将 buf1 的内容复制到 out 中,复制长度为 end1 - buf1
buf1.buffer_memcpy(out, end1 - buf1);
// 将复制的字节数累加到 ret 中
ret += end1 - buf1;
// 如果编码为 UTF8,则直接返回当前 ret 的值
if (enc == ENCODING::UTF8) {
return ret;
}
// 否则,在复制结束后,使用 out 的 buffer_fill_with_zeros_after_index 方法将 end1 - buf1 之后的字节填充为零
out.buffer_fill_with_zeros_after_index(end1 - buf1);
// 返回 ret 的值作为函数结果
return ret;
}
template <ENCODING enc>
static inline npy_intp
string_expandtabs_length(Buffer<enc> buf, npy_int64 tabsize)
{
// 计算 buf 中的代码点数目
size_t len = buf.num_codepoints();
npy_intp new_len = 0, line_pos = 0;
// 复制一份 buf 到 tmp 中
Buffer<enc> tmp = buf;
// 遍历 buf 中的每个代码点
for (size_t i = 0; i < len; i++) {
// 获取当前代码点的值
npy_ucs4 ch = *tmp;
// 如果当前代码点是制表符 '\t'
if (ch == '\t') {
// 如果制表符大小 tabsize 大于 0
if (tabsize > 0) {
// 计算需要增加的长度,使得 line_pos 对齐到 tabsize 的倍数
npy_intp incr = tabsize - (line_pos % tabsize);
line_pos += incr;
new_len += incr;
}
}
else {
// 如果当前代码点不是制表符
line_pos += 1;
// 获取当前代码点在缓冲区中所占的字节数
size_t n_bytes = tmp.num_bytes_next_character();
new_len += n_bytes;
// 如果当前代码点是换行符 '\n' 或回车符 '\r',将行位置重置为 0
if (ch == '\n' || ch == '\r') {
line_pos = 0;
}
}
// 检查 new_len 是否超过 INT_MAX 或为负数,如果是则抛出溢出错误并返回 -1
if (new_len > INT_MAX || new_len < 0) {
npy_gil_error(PyExc_OverflowError, "new string is too long");
return -1;
}
// 指向下一个代码点
tmp++;
}
// 返回计算得到的 new_len 作为函数结果
return new_len;
}
template <ENCODING enc>
static inline npy_intp
string_expandtabs(Buffer<enc> buf, npy_int64 tabsize, Buffer<enc> out)
{
// 计算 buf 中的代码点数目
size_t len = buf.num_codepoints();
npy_intp new_len = 0, line_pos = 0;
// 复制一份 buf 到 tmp 中
Buffer<enc> tmp = buf;
// 遍历 buf 中的每个代码点
for (size_t i = 0; i < len; i++) {
// 获取当前代码点的值
npy_ucs4 ch = *tmp;
// 如果当前代码点是制表符 '\t'
if (ch == '\t') {
// 如果制表符大小 tabsize 大于 0
if (tabsize > 0) {
// 计算需要增加的长度,使得 line_pos 对齐到 tabsize 的倍数
npy_intp incr = tabsize - (line_pos % tabsize);
line_pos += incr;
// 使用 out 的 buffer_memset 方法将 incr 个空格填充到输出缓冲区中
new_len += out.buffer_memset((npy_ucs4) ' ', incr);
// 移动 out 指针,向后移动 incr 个位置
out += incr;
}
}
else {
// 如果当前代码点不是制表符
line_pos++;
// 使用 out 的 buffer_memset 方法将当前代码点 ch 写入输出缓冲区
new_len += out.buffer_memset(ch, 1);
// 移动 out 指针,向后移动一个位置
out++;
// 如果当前代码点是换行符 '\n' 或回车符 '\r',将行位置重置为 0
if (ch == '\n' || ch == '\r') {
line_pos = 0;
}
}
// 指向下一个代码点
tmp++;
}
// 返回计算得到的 new_len 作为函数结果
return new_len;
}
enum class JUSTPOSITION {
CENTER, LEFT, RIGHT
};
template <ENCODING enc>
static inline npy_intp
string_pad(Buffer<enc> buf, npy_int64 width, npy_ucs4 fill, JUSTPOSITION pos, Buffer<enc> out)
{
// 计算最终的字符串宽度,如果 width 小于等于 0,则设为 0
size_t finalwidth = width > 0 ? width : 0;
// 如果 finalwidth 超过了 PY_SSIZE_T_MAX,抛出溢出错误并返回 -1
if (finalwidth > PY_SSIZE_T_MAX) {
npy_gil_error(PyExc_OverflowError, "padded string is too long");
return -1;
}
// 计算 buf 中的代码点数目和字节长度
size_t len_codepoints = buf.num_codepoints();
size_t len_bytes = buf.after - buf.buf;
size_t len;
// 根据编码类型确定长度的计算方式
if (enc == ENCODING::UTF8) {
len = len_bytes;
}
else {
len = len_codepoints;
}
// 如果 buf 的代码点数目大于等于最终宽度,直接将 buf 复制到 out 中并返回长度
if (len_codepoints >= finalwidth) {
buf.buffer_memcpy(out, len);
return (npy_intp) len;
}
size_t left, right;
// 根据对齐方式 pos 计算左右填充量
if (pos == JUSTPOSITION::CENTER) {
size_t pad = finalwidth - len_codepoints;
left = pad / 2 + (pad & finalwidth & 1);
right = pad - left;
}
else if (pos == JUSTPOSITION::LEFT) {
left = 0;
right = finalwidth - len_codepoints;
}
// 在此处继续填写剩余的代码...
}
# 否则分支:计算左侧填充空间和右侧填充空间
else:
left = finalwidth - len_codepoints; # 计算左侧填充空间
right = 0; # 右侧填充空间设为0
# 断言:确保左侧填充空间和右侧填充空间非负
assert(left >= 0 or right >= 0);
# 断言:确保左侧填充后的长度和右侧填充后的长度不会超出最大允许长度
assert(left <= PY_SSIZE_T_MAX - len and right <= PY_SSIZE_T_MAX - (left + len));
# 如果存在左侧填充空间大于0
if (left > 0):
# 将填充字符(fill)写入左侧填充空间,然后向前移动输出位置(out)
out.advance_chars_or_bytes(out.buffer_memset(fill, left));
# 将数据拷贝到输出缓冲区
buf.buffer_memcpy(out, len);
# 更新输出位置,增加已处理的代码点数
out += len_codepoints;
# 如果存在右侧填充空间大于0
if (right > 0):
# 将填充字符(fill)写入右侧填充空间,然后向前移动输出位置(out)
out.advance_chars_or_bytes(out.buffer_memset(fill, right));
# 返回最终输出的宽度
return finalwidth;
// 结束当前的 C++ 函数定义,适用于模板函数和静态内联函数
}
// 定义一个静态内联函数 string_zfill,模板参数为编码类型 enc
template <ENCODING enc>
static inline npy_intp
string_zfill(Buffer<enc> buf, npy_int64 width, Buffer<enc> out)
{
// 计算最终的宽度,如果指定宽度大于0,则使用指定宽度,否则为0
size_t finalwidth = width > 0 ? width : 0;
// 填充字符设为 '0'
npy_ucs4 fill = '0';
// 调用 string_pad 函数对 buf 进行填充,返回填充后的长度
npy_intp new_len = string_pad(buf, width, fill, JUSTPOSITION::RIGHT, out);
// 如果填充失败,返回 -1
if (new_len == -1) {
return -1;
}
// 计算偏移量,将 out 指针偏移 finalwidth - buf.num_codepoints() 个位置
size_t offset = finalwidth - buf.num_codepoints();
Buffer<enc> tmp = out + offset;
// 取出 tmp 指针所指位置的字符
npy_ucs4 c = *tmp;
// 如果字符为 '+' 或 '-',则将 tmp 指针位置的字符改为 fill
if (c == '+' || c == '-') {
tmp.buffer_memset(fill, 1); // 将 tmp 指针位置的字符改为 fill
out.buffer_memset(c, 1); // 将 out 指针位置的字符改为 c
}
// 返回新长度 new_len
return new_len;
}
// 定义一个静态内联函数 string_partition,模板参数为编码类型 enc
template <ENCODING enc>
static inline void
string_partition(Buffer<enc> buf1, Buffer<enc> buf2, npy_int64 idx,
Buffer<enc> out1, Buffer<enc> out2, Buffer<enc> out3,
npy_intp *final_len1, npy_intp *final_len2, npy_intp *final_len3,
STARTPOSITION pos)
{
// 断言编码类型不为 UTF8,如果为 UTF8,则触发错误
assert(enc != ENCODING::UTF8);
// 计算 buf1 和 buf2 的字符数
size_t len1 = buf1.num_codepoints();
size_t len2 = buf2.num_codepoints();
// 如果 buf2 长度为 0,则抛出异常并设置长度返回值为 -1
if (len2 == 0) {
npy_gil_error(PyExc_ValueError, "empty separator");
*final_len1 = *final_len2 = *final_len3 = -1;
return;
}
// 如果 idx 小于 0
if (idx < 0) {
// 根据 pos 的值进行处理
if (pos == STARTPOSITION::FRONT) {
buf1.buffer_memcpy(out1, len1); // 将 buf1 复制到 out1
*final_len1 = len1; // 设置 out1 的最终长度
*final_len2 = *final_len3 = 0; // 设置 out2 和 out3 的最终长度为 0
}
else {
buf1.buffer_memcpy(out3, len1); // 将 buf1 复制到 out3
*final_len1 = *final_len2 = 0; // 设置 out1 和 out2 的最终长度为 0
*final_len3 = len1; // 设置 out3 的最终长度
}
return;
}
// 将 buf1 的前 idx 个字符复制到 out1
buf1.buffer_memcpy(out1, idx);
*final_len1 = idx; // 设置 out1 的最终长度为 idx
// 将 buf2 复制到 out2
buf2.buffer_memcpy(out2, len2);
*final_len2 = len2; // 设置 out2 的最终长度为 len2
// 将 buf1 的 idx+len2 到末尾的字符复制到 out3
(buf1 + idx + len2).buffer_memcpy(out3, len1 - idx - len2);
*final_len3 = len1 - idx - len2; // 设置 out3 的最终长度
}