Cython:如何转换包含赋值的 C 宏

Cython: How to convert C macro containing assignments

假设我有以下要转换为 Cython 的 C 宏。问题是第二个和第三个修改了它们的参数值。如何将其转换为 Cython 中的等效项?

示例(siphash24中使用的一些宏):

#define ROTATE(x, b) (uint64_t)( ((x) << (b)) | ( (x) >> (64 - (b))) )

#define HALF_ROUND(a,b,c,d,s,t)                 \
        a += b; c += d;                         \
        b = ROTATE(b, s) ^ a;                   \
        d = ROTATE(d, t) ^ c;                   \
        a = ROTATE(a, 32);

#define DOUBLE_ROUND(v0,v1,v2,v3)               \
        HALF_ROUND(v0,v1,v2,v3,13,16);          \
        HALF_ROUND(v2,v1,v0,v3,17,21);          \
        HALF_ROUND(v0,v1,v2,v3,13,16);          \
        HALF_ROUND(v2,v1,v0,v3,17,21);

我试过的:

我开始使用 f-strings 扩展宏,但它有点乱:

def ROTATE(x, b):
    return f'<uint64_t>( (({x}) << ({b})) | ( ({x}) >> (64 - ({b}))) )'

def HALF_ROUND(a, b, c, d, s, t):
    return f"""
    {a} += {b}
    {c} += {d}
    {b} = {ROTATE(b, s)} ^ {a}
    {d} = {ROTATE(d, t)} ^ {c}
    {a} = {ROTATE(a, 32)}
"""

def DOUBLE_ROUND(v0, v1, v2, v3):
    return f"""
    {HALF_ROUND(v0,v1,v2,v3,13,16)}
    {HALF_ROUND(v2,v1,v0,v3,17,21)}
    {HALF_ROUND(v0,v1,v2,v3,13,16)}
    {HALF_ROUND(v2,v1,v0,v3,17,21)}
    """

因此,例如,DOUBLE_ROUND('v0','v1','v2','v3') 扩展为:

    v0 += v1
    v2 += v3
    v1 = <uint64_t>( ((v1) << (13)) | ( (v1) >> (64 - (13))) ) ^ v0
    v3 = <uint64_t>( ((v3) << (16)) | ( (v3) >> (64 - (16))) ) ^ v2
    v0 = <uint64_t>( ((v0) << (32)) | ( (v0) >> (64 - (32))) )

    
    v2 += v1
    v0 += v3
    v1 = <uint64_t>( ((v1) << (17)) | ( (v1) >> (64 - (17))) ) ^ v2
    v3 = <uint64_t>( ((v3) << (21)) | ( (v3) >> (64 - (21))) ) ^ v0
    v2 = <uint64_t>( ((v2) << (32)) | ( (v2) >> (64 - (32))) )

    
    v0 += v1
    v2 += v3
    v1 = <uint64_t>( ((v1) << (13)) | ( (v1) >> (64 - (13))) ) ^ v0
    v3 = <uint64_t>( ((v3) << (16)) | ( (v3) >> (64 - (16))) ) ^ v2
    v0 = <uint64_t>( ((v0) << (32)) | ( (v0) >> (64 - (32))) )

    
    v2 += v1
    v0 += v3
    v1 = <uint64_t>( ((v1) << (17)) | ( (v1) >> (64 - (17))) ) ^ v2
    v3 = <uint64_t>( ((v3) << (21)) | ( (v3) >> (64 - (21))) ) ^ v0
    v2 = <uint64_t>( ((v2) << (32)) | ( (v2) >> (64 - (32))) )

是否有更好的(输出更易读)的方法?

如果您乐于使用 cdef 函数(即只能从 Cython 调用),那么惯用的方法可能是传递指针而不是值:

cdef void half_round(int* a, int* b, int* c, int* d, int s, int t):
    a[0] += b[0]; c[0] += d[0]
    b[0] = ROTATE(b[0], s) ^ a[0]
    d[0] = ROTATE(d[0], t) ^ c[0]
    a[0] = ROTATE(a[0], 32)

您必须取消对指针的引用,这可以像我在此处所做的那样使用 [0]cython.operator.dereference 来完成。你会称它为

half_round(&a, &b, &c, &d, s, t)

如果你想让它更灵活以便它适用于 int 以外的类型,你可以使用 fused types

如果您希望函数可以从 Python 调用,那么您需要使用 def 函数,这会带来 Python 数字不可变的问题。最好的方法可能涉及返回新值的元组:

def half_round(a, b, c, d, s, t):
    a += b; c += d
    b = rotate(b, s)^a
    d = rotate(d, t)^c
    a = rotate(a, 32)
    return (a, b, c, d)

你称之为:

a, b, c, d = half_round(a, b, c, d, s, t)