开开 发表于 2009-7-30 15:40

yy使用C语言为Python编写纠错码模块

不累的王

普通会员

发贴: 339
积分: 0
来自:
注册日期: 2006-05-26
发表时间: 2008-04-10 02:26:40

--------------------------------------------------------------------------------
yy使用C语言为Python编写纠错码模块



去年一个项目的开发中因为需要使用到纠错码,所以自然而然的接触到了rscode。

rscode 是Reed-Solomon纠错算法的一种实现,被广泛的使用于Audio CD和CD-ROM。关于rscode更详细
的描述,请前往其官方网站查看(http://rscode.sourceforge.net/)。

rscode 使用标准C编写,优点自不必说,跨平台、执行效率高。可C代码也有其固有的麻烦,每次调试都
需要编译,开发过程远不如脚本语言来得阳春白雪。于是乎,笔者生出一个想法:何不将其编译为Python模
块?这样在测试程序逻辑的时候可以省去不少的麻烦。现在将过程记录如下,希望对大家有所帮助。

首先在官方网站下载源文件。
解压源代码,并将其编译为静态链接库。



项目的环境为Win32,不过因为笔者的拧巴性格,还是使用了MingGW系列的工具对其进行编译。
执行make,得到静态链接库:
libecc.a


接下来开始为我们的模块编写C源码。


        /*
       *        RsCode.c
       */

        #include <python.h>
        #include "ecc.h"

        //编码部分
        //包裹函数调用的实际编码函数
        //参数依次为:原文buffer指针,buffer长度,得到的rscode编码串的指针
        void _encode(char* data, unsigned long size, char* codeword)
        {
                //调用rscode库提供的两个函数,分别完成初始化和编码工作。
                initialize_ecc();
                encode_data(data, size, codeword);
        }

        //编码部分的包裹函数,这个函数将和CPython的虚拟机进行亲密接触 :)
        //函数参数的定义。self为当前模块的引用;args为传入这个函数的参数,可以是任意数据类型。当
然,如果传入参数与处理过程不匹配的话,就会产生一个异常。
        PyObject* wrap_encode(PyObject* self, PyObject*args)
        {
                char*        data;
                int         data_size;
                char        codeword;

                PyObject* resString;//储存编码结果的Python字符串对象。
               
                //使用Python/C API中Tuple对象的方法,得到指定顺序的参数。
                //在这个函数中,我们将参数视为一个列表,其中第一个元素为数据的引用,第二个元素为数据
长度。
                data = PyString_AsString(PyTuple_GetItem(args, 0));
                data_size = PyInt_AsLong(PyTuple_GetItem(args, 1));

                //调用前面定义的编码函数,将结果存入codeword中。
                _encode(data, data_size, codeword);

                //构造Python中的Buffer对象。
                //PyString_FromStringAndSize这个函数是笔者从Python/C API的几角旮旯中找到的,它将严格
按照输入数据的指针和长度构造PythonBuffer对象,而不会像一般的PythonString对象构造API一样遇到“\n”就
结束了。这函数恐怕是大家最需要的了,呵呵。
                resString = PyString_FromStringAndSize(codeword, data_size + NPAR);
               
                //OK这个函数最终将返回生成的Python Buffer对象。
                return resString;
        }
       
        //解码部分
        //格式方面照猫画虎即可,解码时调用的rscode库中的方法,参照了rscode自带的例子。
        void _decode(char* data, unsigned long data_size)
        {
                initialize_ecc();
                decode_data(data, data_size);
                correct_errors_erasures(data, data_size, 0, 0);
        }

        //同样需要编写一个返回Python数据类型的包裹函数。
        PyObject* wrap_decode(PyObject* self, PyObject* args)
        {
                char*        data;
                int        data_size;
       
                PyObject* resString;

                data = PyString_AsString(PyTuple_GetItem(args, 0));
                data_size = PyInt_AsLong(PyTuple_GetItem(args, 1));

                _decode(data, data_size);

                resString = PyString_FromStringAndSize(data, data_size);

                return resString;
        }

        //定义这个模块所包含的方法,格式如下的
        static PyMethodDef        RsCodeMethods[] =
        {
                //分别声明方法在Python中的名字,实际执行中调用的C包裹函数名,以及Python函数说明文
档。
                {"encode", wrap_encode, METH_VARARGS,
               "encode(str, str_size)\n    decode data with rscode.\nlen(data) + NPAR < 256\n"
                },

                {"decode", wrap_decode, METH_VARARGS,
               "decode(codeword, codeword_size)\n    decode data with rscode.\n"
                },

                //以两个NULL作为声明的结束。
                {NULL, NULL}
        };

        //模块在被引用时,将调用的初始化函数。作用是向虚拟机注册模块中的对象。注意函数名称的格式。
        void initRsCode()
        {
                PyObject*        m;
               
                m = Py_InitModule("RsCode", RsCodeMethods);
        }

        /*
       *        End Of RsCode.c
       */




下面完成RsCode.c的编译。


        #
        #        Makefile for RsCode.pyd
        #

        # ecc.h 所在的目录。
        RSHEAD_FLAG = -I"d:\ts\sm\rscode-1.0"       

        # libecc.a 路径。
        RS_LIB = d:\ts\sm\rscode-1.0\libecc.a       

        # Python头文件所在位置
        PYHEAD_FLAG = -I"C:\Python24\include"       

        # Python虚拟机静态库路径
        PY_LIB = c:\Python24\libs\libpython24.a        #

        # 我们的目标文件
        TARGETS = RsCode.pyd

        all:
                gcc $(RSHEAD_FLAG) $(RS_LI $(PYHEAD_FLAG) $(PY_LI -shared -o $(TARGETS)

        # End of Makefile

        从Makefile不难看出,我们编译后得到的模块RsCode.pyd其实是一个动态链接库。



最后,让我们来看看如何使用这个刚刚得到的模块。
首先保证RsCode.pyd在Python安装目录下的Lib目录下,或是和调用这个模块的Python脚本在同一个目
录,以保证Python能够找到这个模块。

首先,通过import关键字加载这个模块。


        >>> import RsCode

        看看里面有什么?是不是我们定义的两个Python函数?
        >>> dir(RsCode)
        ['__doc__', '__file__', '__name__', 'decode', 'encode']

        看看函数的帮助文档怎么说?
        >>> help(RsCode.decode)
        Help on built-in function decode in module RsCode:

        decode(...)
          decode(codeword, codeword_size)
          decode data with rscode.


        定义一个字符串Buffer
        >>> src = 'hello, world'

        用rscode进行纠错编码。
        >>> codeword = RsCode.encode(src, len(src))
       
        看,末尾的4个字节就是rscode加上的监督位。
        >>> codeword
        'hello, world\xeb\t\xa1\xe7'

        (注: rscode的纠错能力与监督位的长短有关,对于相同长度信息,监督位越多,纠错效果越好。监督位
长度 NPAR 可以在rscode的源码中找到,修改它的值再编译即可得到拥有相应长度监督位的rscode库。默认状态
下监督位长为4。CD-ROM的rscode使用24/28的rscode,即28字节长度的编码中含有24位有效信息及4位监督位。)

        让我们制造一些错误,用字符'z'替换掉字符'r'。
        >>> error = codeword.replace('r', 'z')

        原文中的"world"变成了"wozld"
        >>> error
        'hello, wozld\xeb\t\xa1\xe7'

        使用解码函数进行纠错。
        >>> RsCode.decode(error, len(error))
        'hello, world\xeb\t\xa1\xe7'

        因为,解码函数修改了那个包含错误的buffer,所以字符串error的值也改变了。
        最终,error中储存内容为原来的正确内容加上监督位。
        >>> error
        'hello, world\xeb\t\xa1\xe7'


OK,一切正常。

以上就是笔者编译这个模块的全部过程,语焉不详之处,请查阅参考文献。


        通过使用这个模块,俺顺利地使用Python完成了程序的原型设计。俺觉得,这要比在Python中使用
os.system()函数调用外部程序的dirty方法优雅多了。常言道“磨刀不误砍柴功”,如果在其他项目的编写中也使
用这种方法,应该可以大大减少开发中的工作量,如果引擎也能提供一个Python模块……浮想联翩中……
                       
        故事讲完了,希望本文为大家在新的春天里的工作有所帮助,另外也希望笔者能够骗到多多稿费!


参考文献:
Extending and Embedding the Python Interpreter
http://docs.python.org/ext/ext.html

Python/C API Reference Manual
http://docs.python.org/api/api.html









ps1:

标题中的yy表示原创


ps2:

郁闷,没赶上这期的炖排……

等下期出来的时候就得承受由CPI上涨造成的货币贬值了,囧TL








<---- 以上言论仅代表本人立场 ---->



__________________


  “……机器人罗诺,现在我要对你下达最新指令了。”
  “是,主人。我都等了三万年了呢。”
  “你还记得回地球的航路吧?”
页: [1]
查看完整版本: yy使用C语言为Python编写纠错码模块