是什么导致我的Python紧缩C扩展崩溃?

问题描述 投票:1回答:1

我正在尝试将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);
}

python c++
1个回答
1
投票

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*解析参数的所有位置。

© www.soinside.com 2019 - 2024. All rights reserved.