2010-12-14 15 views
3

私はPython 3でそれを使用できるように、mcryptライブラリをラップするためにCPythonにCython拡張を書き込もうとしています。しかし、私は使用しようとしている間segfault mcrypt APIの1つ。CythonのバイトからCのchar *

失敗しているコードは:

def _real_encrypt(self, source): 
    src_len = len(source) 
    cdef char* ciphertext = source 
    cmc.mcrypt_generic(self._mcStream, <void *>ciphertext, src_len) 
    retval = source[:src_len] 
    return retval 

ここで、IはCythonドキュメントを理解する方法、3行目の割り当てはCにバッファ(Pythonの3オブジェクト)の内容をコピーする必要があります文字列ポインタ。私は、これはまた、それがメモリを割り当てることを意味することを理解だろうが、私はこの変更をしたとき:

def _real_encrypt(self, source): 
    src_len = len(source) 
    cdef char* ciphertext = <char *>malloc(src_len) 
    ciphertext = source 
    cmc.mcrypt_generic(self._mcStream, <void *>ciphertext, src_len) 
    retval = source[:src_len] 
    return retval 

は、それはまだセグメンテーションフォルトで墜落しました。 mcrypt_genericの内部でクラッシュしていますが、プレーンなCコードを使用するとうまく動作するようになりました。そこでCythonがCのデータをどのように扱っているのかよく分かりません。

ありがとうございました!

ETA:問題は私の一部のバグでした。私はあまりにも多くの時間のために目を覚ました後、これについて取り組んでいました。私が今持っているコード、作品は、次のとおりです。

def _real_encrypt(self, source): 
    src_len = len(source) 
    cdef char *ciphertext = <char *>malloc(src_len) 
    cmc.strncpy(ciphertext, source, src_len) 
    cmc.mcrypt_generic_init(self._mcStream, <void *>self._key, 
          len(self._key), NULL) 
    cmc.mcrypt_generic(self._mcStream, <void *>ciphertext, 
         src_len) 

    retval = ciphertext[:src_len] 
    cmc.mcrypt_generic_deinit(self._mcStream) 
    return retval 

それが戻り値に暗号化した後、第2のコピーを行うためのコピーを作成してそれは、おそらく世界で最も効率的なコードではありません。しかし、それを避けることができるかどうかはわかりません。なぜなら、新しく割り当てられたバッファを取り、それをPythonのバイトコードとして返すことが可能かどうかわからないからです。しかし、私は実際の機能を持っているので、私はブロックごとの方法も実装するつもりです。そのため、暗号化または復号化のためのブロックの反復可能性を提供し、ソース全体を持たなくてもできるようになりますと宛先すべてを一度にすべてのメモリに格納することができます。その方法では、巨大なファイルを暗号化/復号化することが可能になります...

助けてくれてありがとう、みんな!

def _real_encrypt(self,char* src): 
    ... 

答えて

3

+0

...これは、私が多忙の間にプログラムしようとしていたので、私がおそらく昨晩寝るべきだったのでしょう。確かに、それを動作させるためには、strncpy(入力でNULLバイトの可能性があるために使用した)を呼び出してから、mcrypt_genericへの呼び出しを行い、出力をPythonバイトレットにコピーし、一時バッファ、およびリターン。この答えをありがとう、それは正しい方向に私を指摘した。 –

+0

!!! 'strncpy'は、入力にNULLバイトが有効になる可能性がある場合には**あなたには役立ちません。つまり、入力は実際には文字列ではなく、一連のバイトであることを意味します。 'memcpy'などを使ってください。 –

+0

ああ、あなたは間違いない。 nは「最大」です。やばい。ああ、ああ、うんざり。私は別の質問に投稿したバグを指摘したばかりかもしれないと思う:http://stackoverflow.com/questions/4451977/data-corruption-wheres-the-bug –

1

私は(Pythonの2.xので)使用したアプローチは、Cythonコードがすべての変換を行うよう関数シグネチャの文字列型パラメータを宣言し、自動的を型チェックします最初のものはchar*をPython文字列で指しています。 2番目の引数はメモリを割り当てますが、ポインタをPython文字列に戻し、新しく割り当てられたメモリを無視します。おそらくCythonからCライブラリ関数strcpyを呼び出す必要があります。私は詳細を知らない。

3

あなたのコードを改善するのに役立ついくつかのコメント、IMHO。 Python C APIが提供する関数があります。必要な処理を行い、すべてがPythonのやり方に準拠していることを確認します。埋め込まれたNULLは問題なく処理されます。

直接ではなくmallocを呼び出して、これを変更:

cdef char *ciphertext = <char *>malloc(src_len) 

cdef str retval = PyString_FromStringAndSize(PyString_AsString(source), <Py_ssize_t>src_len) 
cdef char *ciphertext = PyString_AsString(retval) 

に上記の行は、 sourceの内容に初期化されたブランドの新しいPythonのstrのオブジェクトを作成します。 2行目は、コピーしないで ciphertextretvalの内部 char *バッファを指しています。 ciphertextを変更すると、 retvalが変更されます。 retvalは新しいPythonのstrなので、 _real_encryptから返される前にCコードで変更することができます。

herehereの詳細については、上記の関数のPython C/APIドキュメントを参照してください。

ネットエフェクトはコピーを保存します。全体のコードは次のようなものになります:

cdef extern from "Python.h": 
    object PyString_FromStringAndSize(char *, Py_ssize_t) 
    char *PyString_AsString(object) 

def _real_encrypt(self, source): 
    src_len = len(source) 
    cdef str retval = PyString_FromStringAndSize(PyString_AsString(source), <Py_ssize_t>src_len) 
    cdef char *ciphertext = PyString_AsString(retval) 
    cmc.mcrypt_generic_init(self._mcStream, <void *>self._key, 
          len(self._key), NULL) 
    cmc.mcrypt_generic(self._mcStream, <void *>ciphertext, 
         src_len) 
    # since the above initialized ciphertext, the retval str is also correctly initialized, too. 
    cmc.mcrypt_generic_deinit(self._mcStream) 
    return retval 
関連する問題