我正在尝试将crunch作为C ++编写包装。
内部代码似乎工作正常,但是收益不起作用-所有这些都会导致崩溃。代码一直运行到返回为止,然后崩溃。
我添加了一个虚拟函数来测试问题是否在于返回值本身,但这似乎并非如此(请参阅虚拟函数)。我还尝试了不同的返回值,包括空的回报,但所有这些都会导致崩溃。
static PyObject * dummy(PyObject * self, PyObject * args) {
return Py_BuildValue("{s:I,s:I,s:I,s:I,s:I,s:I,s:I,s:I}",
"width", 0,
"height", 0,
"levels", 0,
"faces", 0,
"bytes_per_block", 0,
"userdata0", 0,
"userdata1", 0,
"format", 0
);
}
// texture info
static PyObject * get_texture_info(PyObject * self, PyObject * args) {
unsigned char * buf;
UINT32 buf_length;
if (!PyArg_ParseTuple(args, "y*I", & buf, & buf_length)) {
return NULL;
}
crnd::crn_texture_info texture_info;
if (crnd::crnd_get_texture_info(buf, buf_length, & texture_info) == 0) {
PyErr_Format(PyExc_ZeroDivisionError, "Dividing %d by zero!", buf_length);
return NULL;
}
fprintf(stderr, "width: %d\n", texture_info.m_width);
fprintf(stderr, "height: %d\n", texture_info.m_height);
fprintf(stderr, "levels: %d\n", texture_info.m_levels);
fprintf(stderr, "faces: %d\n", texture_info.m_faces);
fprintf(stderr, "bytes_per_block: %d\n", texture_info.m_bytes_per_block);
fprintf(stderr, "userdata0: %d\n", texture_info.m_userdata0);
fprintf(stderr, "userdata1: %d\n", texture_info.m_userdata1);
fprintf(stderr, "format: %d\n", texture_info.m_format);
return Py_BuildValue("{s:I,s:I,s:I,s:I,s:I,s:I,s:I,s:I}",
"width", texture_info.m_width,
"height", texture_info.m_height,
"levels", texture_info.m_levels,
"faces", texture_info.m_faces,
"bytes_per_block", texture_info.m_bytes_per_block,
"userdata0", texture_info.m_userdata0,
"userdata1", texture_info.m_userdata1,
"format", texture_info.m_format
);
}
D:\Projects\python_c\decrunch>python
Python 3.7.4 (tags/v3.7.4:e09359112e, Jul 8 2019, 20:34:20) [MSC v.1916 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import crunch
>>> d = open("tests\\res\\dxt1.crn",'rb').read()
>>> crunch.dummy()
{'width': 0, 'height': 0, 'levels': 0, 'faces': 0, 'bytes_per_block': 0, 'userdata0': 0, 'userdata1': 0, 'format': 0}
>>> crunch.get_texture_info(d,len(d))
width: 128
height: 128
levels: 8
faces: 1
bytes_per_block: 8
userdata0: 0
userdata1: 0
format: 0
D:\Projects\python_c\decrunch>python
我想知道如何解决此问题。
完整代码:
#include <Python.h>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#else
/* For System that are not Windows, we'll need to define these. */
#if SIZEOF_SHORT == 2
#define INT16 short
#elif SIZEOF_INT == 2
#define INT16 int
#else
#define INT16 short /* most things works just fine anyway... */
#endif
#if SIZEOF_SHORT == 4
#define INT32 short
#elif SIZEOF_INT == 4
#define INT32 int
#elif SIZEOF_LONG == 4
#define INT32 long
#else
#error Cannot find required 32-bit integer type
#endif
#if SIZEOF_LONG == 8
#define INT64 long
#elif SIZEOF_LONG_LONG == 8
#define INT64 long
#endif
#define INT8 signed char
#define UINT8 unsigned char
#define UINT16 unsigned INT16
#define UINT32 unsigned INT32
#endif
/* assume IEEE; tweak if necessary (patches are welcome) */
#define FLOAT16 UINT16
#define FLOAT32 float
#define FLOAT64 double
#ifdef _MSC_VER
typedef signed __int64 int64_t;
#endif
#ifdef __GNUC__
#define GCC_VERSION (__GNUC__ * 10000 \
+ __GNUC_MINOR__ * 100 \
+ __GNUC_PATCHLEVEL__)
#endif
#include <string.h>
#if !defined(__APPLE__)
#if defined(__FreeBSD__)
#include <stdlib.h>
#else
#include <malloc.h>
#endif
#ifdef _WIN32
#define malloc_usable_size _msize
#endif
#endif
//#define CRND_HEADER_FILE_ONLY
#include "crunch/crn_decomp.h"
static PyObject * dummy(PyObject * self, PyObject * args) {
return Py_BuildValue("{s:I,s:I,s:I,s:I,s:I,s:I,s:I,s:I}",
"width", 0,
"height", 0,
"levels", 0,
"faces", 0,
"bytes_per_block", 0,
"userdata0", 0,
"userdata1", 0,
"format", 0
);
}
// texture info
static PyObject * get_texture_info(PyObject * self, PyObject * args) {
unsigned char * buf;
UINT32 buf_length;
if (!PyArg_ParseTuple(args, "y*I", & buf, & buf_length)) {
return NULL;
}
crnd::crn_texture_info texture_info;
if (crnd::crnd_get_texture_info(buf, buf_length, & texture_info) == 0) {
PyErr_Format(PyExc_ZeroDivisionError, "Dividing %d by zero!", buf_length);
return NULL;
}
fprintf(stderr, "width: %d\n", texture_info.m_width);
fprintf(stderr, "height: %d\n", texture_info.m_height);
fprintf(stderr, "levels: %d\n", texture_info.m_levels);
fprintf(stderr, "faces: %d\n", texture_info.m_faces);
fprintf(stderr, "bytes_per_block: %d\n", texture_info.m_bytes_per_block);
fprintf(stderr, "userdata0: %d\n", texture_info.m_userdata0);
fprintf(stderr, "userdata1: %d\n", texture_info.m_userdata1);
fprintf(stderr, "format: %d\n", texture_info.m_format);
return Py_BuildValue("{s:I,s:I,s:I,s:I,s:I,s:I,s:I,s:I}",
"width", texture_info.m_width,
"height", texture_info.m_height,
"levels", texture_info.m_levels,
"faces", texture_info.m_faces,
"bytes_per_block", texture_info.m_bytes_per_block,
"userdata0", texture_info.m_userdata0,
"userdata1", texture_info.m_userdata1,
"format", texture_info.m_format
);
}
// level info
static PyObject * get_level_info(PyObject * self, PyObject * args) {
unsigned char * buf;
UINT32 buf_length;
UINT32 level_index;
if (!PyArg_ParseTuple(args, "y*II", & buf, & buf_length, & level_index)) {
return NULL;
}
crnd::crn_level_info level_info;
if (crnd::crnd_get_level_info(buf, buf_length, level_index, & level_info) == 0) {
PyErr_Format(PyExc_ZeroDivisionError, "Dividing %d by zero!", level_index);
return NULL;
}
return Py_BuildValue("{s:I,s:I,s:I,s:I,s:I,s:I,s:I}",
"width", level_info.m_width,
"height", level_info.m_height,
"faces", level_info.m_faces,
"blocks_x", level_info.m_blocks_x,
"blocks_y", level_info.m_blocks_y,
"bytes_per_block", level_info.m_bytes_per_block,
"format", level_info.m_format
);
}
// unpack level
static PyObject * unpack_level(PyObject * self, PyObject * args) {
unsigned char * buf;
UINT32 buf_length;
UINT32 level_index;
if (!PyArg_ParseTuple(args, "y*II", & buf, & buf_length, & level_index)) {
return NULL;
}
crnd::crn_level_info level_info;
if (crnd::crnd_get_level_info(buf, buf_length, level_index, & level_info) == 0) {
PyErr_Format(PyExc_ZeroDivisionError, "Dividing %d by zero!", level_index);
return NULL;
}
UINT32 bpb = level_info.m_bytes_per_block;
crnd::crnd_unpack_context ctx;
unsigned char * dst;
void * ppDst[6];
ppDst[0] = dst;
UINT32 dst_length = bpb * level_info.m_blocks_x * level_info.m_blocks_y;
ctx = crnd::crnd_unpack_begin(buf, buf_length);
if (crnd::crnd_unpack_level(ctx, ppDst, dst_length, bpb, level_index) == 0) {
fprintf(stderr, "unpack failed");
PyErr_Format(PyExc_ZeroDivisionError, "Dividing %d by zero!", dst_length);
return NULL;
}
fprintf(stderr, "pre ret");
return Py_BuildValue("y*", dst);
}
// Exported methods are collected in a table
static struct PyMethodDef method_table[] = {
{
"dummy",
(PyCFunction) dummy,
METH_VARARGS,
"Method docstring"
},
{
"get_texture_info",
(PyCFunction) get_texture_info,
METH_VARARGS,
"Method docstring"
},
{
"get_level_info",
(PyCFunction) get_level_info,
METH_VARARGS,
"Method docstring"
},
{
"unpack_level",
(PyCFunction) unpack_level,
METH_VARARGS,
"Method docstring"
},
{
NULL,
NULL,
0,
NULL
} // Sentinel value ending the table
};
// A struct contains the definition of a module
static PyModuleDef crunch_module = {
PyModuleDef_HEAD_INIT,
"crunch", // Module name
"This is the module docstring",
-1, // Optional size of the module state memory
method_table,
NULL, // Optional slot definitions
NULL, // Optional traversal function
NULL, // Optional clear function
NULL // Optional module deallocation function
};
// The module init function
PyMODINIT_FUNC PyInit_crunch(void) {
return PyModule_Create( & crunch_module);
}
如https://docs.python.org/3/c-api/arg.html似乎表明,y*
应该将数据放入Py_buffer
。
但是那是什么? https://docs.python.org/3/c-api/buffer.html似乎可以解释这一点:它是一个具有大量有用字段的结构。它仍然绑定到原始对象。使用后必须将其释放。
对您来说,这意味着您必须进行以下更改:
替换
unsigned char* buf;
with
Py_buffer buf;
及更高版本
crnd::crnd_get_texture_info(buf, buf_length, &texture_info)
with
crnd::crnd_get_texture_info(buf.buf, buf_length, &texture_info)
或者,为了消除传递len()
的需要,也许甚至更好
crnd::crnd_get_texture_info(buf.buf, buf.len, &texture_info)
((但请参阅文档,以了解您是否可以连续使用此buf
;似乎有限制)
使用后,必须释放此缓冲区(PyBuffer_Release()
)。
如果这看起来太复杂,则应使用y#
格式,该格式为您提供两个值:带有数据的const char *
和长度的int
。
在这里,你会做的
unsigned char* buf;
int buf_length; // or Py_ssize_t? See below.or not?
if (!PyArg_ParseTuple(args, "y#", &buf, &buf_length)) {
return NULL;
}
并继续操作。
两个版本都从传递的对象中得出长度,所以不要用crunch.get_texture_info(d,len(d))
来调用它,而要用crunch.get_texture_info(d)
来调用。
是否需要将buf_length
定义为int
还是Py_ssize_t
取决于是否在包含PY_SSIZE_T_CLEAN
之前定义了Python.h
(正如在文档中“注释”框中所说的那样)。它还说:“最好总是定义PY_SSIZE_T_CLEAN
。”
免责声明:这些仅是可以继续进行操作的提示。我只有一个模糊的想法,这实际上是如何工作的,但是在我看来,这可能指向正确的方向。绝对不是直接可用的解决方案。
编辑后编辑:此解决方案应应用于当前使用y*
解析参数的所有位置。