gpt4 book ai didi

python - 如何在 cython 中执行 struct.pack 和 struct.unpack?

转载 作者:太空狗 更新时间:2023-10-30 00:50:13 25 4
gpt4 key购买 nike

我正在尝试将 python 模块转换为 cython,它做了很多序列化和反序列化工作。

目前我必须这样做:

import struct

from libc.stdint cimport (
int32_t,
int64_t,
)

cpdef bytes write_int(int32_t i):
return struct.pack("!i", i)

cpdef bytes write_long(int64_t i):
return struct.pack("!q", i)

cdef bytes write_double(double val):
return struct.pack("!d", val)

cdef bytes write_string(bytes val):
cdef int32_t length = len(val)
cdef str fmt
fmt = "!i%ds" % length
return struct.pack(fmt, length, val)

c lib 中是否有等同于 struct.pack 和 struct.unpack 的东西?在 cython 中执行此类操作的最佳方法是什么?

最佳答案

我查看了模块( thisthis )并将代码翻译成 Cython 并删除了 PyObject 部分。理论上这应该可行,但有些部分(如 float 部分)我无法严格测试:

一些导入:

from cpython.array cimport array, clone
from libc.string cimport memcmp, memcpy
from libc.math cimport frexp, ldexp
from libc.stdint cimport int32_t, int64_t

使用融合类型保存一些代码。它在技术上不是一个稳定的功能,但它对我来说完美无瑕:

ctypedef fused integer:
int32_t
int64_t

这部分测试机器的字节顺序。它对我有用,但这不是一个完整的套件。 OTOH,看起来不错

cdef enum float_format_type:
unknown_format,
ieee_big_endian_format,
ieee_little_endian_format

# Set-up
cdef array stringtemplate = array('B')
cdef float_format_type double_format

cdef double x = 9006104071832581.0

if sizeof(double) == 8:
if memcmp(&x, b"\x43\x3f\xff\x01\x02\x03\x04\x05", 8) == 0:
double_format = ieee_big_endian_format
elif memcmp(&x, b"\x05\x04\x03\x02\x01\xff\x3f\x43", 8) == 0:
double_format = ieee_little_endian_format
else:
double_format = unknown_format

else:
double_format = unknown_format;

(stringtemplate 用于快速生成bytes 对象)

这部分很简单:

cdef void _write_integer(integer x, char* output):
cdef int i
for i in range(sizeof(integer)-1, -1, -1):
output[i] = <char>x
x >>= 8

cpdef bytes write_int(int32_t i):
cdef array output = clone(stringtemplate, sizeof(int32_t), False)
_write_integer(i, output.data.as_chars)
return output.data.as_chars[:sizeof(int32_t)]

cpdef bytes write_long(int64_t i):
cdef array output = clone(stringtemplate, sizeof(int64_t), False)
_write_integer(i, output.data.as_chars)
return output.data.as_chars[:sizeof(int64_t)]

array 类似于 malloc 但它是垃圾回收的:)。

这部分我基本上不知道。我的“测试”通过了,但这主要是希望:

cdef void _write_double(double x, char* output):
cdef:
unsigned char sign
int e
double f
unsigned int fhi, flo, i
char *s

if double_format == unknown_format or True:
if x < 0:
sign = 1
x = -x

else:
sign = 0

f = frexp(x, &e)

# Normalize f to be in the range [1.0, 2.0)

if 0.5 <= f < 1.0:
f *= 2.0
e -= 1

elif f == 0.0:
e = 0

else:
raise SystemError("frexp() result out of range")

if e >= 1024:
raise OverflowError("float too large to pack with d format")

elif e < -1022:
# Gradual underflow
f = ldexp(f, 1022 + e)
e = 0;

elif not (e == 0 and f == 0.0):
e += 1023
f -= 1.0 # Get rid of leading 1

# fhi receives the high 28 bits; flo the low 24 bits (== 52 bits)
f *= 2.0 ** 28
fhi = <unsigned int>f # Truncate

assert fhi < 268435456

f -= <double>fhi
f *= 2.0 ** 24
flo = <unsigned int>(f + 0.5) # Round

assert(flo <= 16777216);

if flo >> 24:
# The carry propagated out of a string of 24 1 bits.
flo = 0
fhi += 1
if fhi >> 28:
# And it also progagated out of the next 28 bits.
fhi = 0
e += 1
if e >= 2047:
raise OverflowError("float too large to pack with d format")

output[0] = (sign << 7) | (e >> 4)
output[1] = <unsigned char> (((e & 0xF) << 4) | (fhi >> 24))
output[2] = 0xFF & (fhi >> 16)
output[3] = 0xFF & (fhi >> 8)
output[4] = 0xFF & fhi
output[5] = 0xFF & (flo >> 16)
output[6] = 0xFF & (flo >> 8)
output[7] = 0xFF & flo

else:
s = <char*>&x;

if double_format == ieee_little_endian_format:
for i in range(8):
output[i] = s[7-i]

else:
for i in range(8):
output[i] = s[i]

如果你能理解它是如何工作的,一定要自己检查一下。

然后我们像以前一样包装它:

cdef bytes write_double(double x):
cdef array output = clone(stringtemplate, sizeof(double), False)
_write_double(x, output.data.as_chars)
return output.data.as_chars[:sizeof(double)]

string one 其实很简单,解释了为什么我要像上面那样设置它:

cdef bytes write_string(bytes val):
cdef:
int32_t int_length = sizeof(int32_t)
int32_t input_length = len(val)
array output = clone(stringtemplate, int_length + input_length, True)

_write_integer(input_length, output.data.as_chars)
memcpy(output.data.as_chars + int_length, <char*>val, input_length)

return output.data.as_chars[:int_length + input_length]

关于python - 如何在 cython 中执行 struct.pack 和 struct.unpack?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23108356/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com