Я пытаюсь написать расширение Cython для CPython, чтобы обернуть библиотеку mcrypt, чтобы я мог использовать его с Python 3. Однако я столкнулся с проблемой, когда я segfault при попытке использовать один из API-интерфейсов mcrypt.Cython bytes to 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
Теперь, как я понимаю документацию Cython, назначение в строке 3 следует скопировать содержимое буфера (объекта в Python 3) в C указатель строки. Я полагаю, что это также будет означать, что он будет выделять память, но когда я сделал эту модификацию:
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
он все еще разбился с Segfault. Это сбой внутри 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
Это, вероятно, не самый эффективный код в мире, как это делает копию, чтобы сделать шифрование, а затем второй экземпляр для возвращаемого значения. Я не уверен, что можно избежать этого, хотя, поскольку я не уверен, можно ли взять вновь выделенный буфер и вернуть его на Python на месте как bytestring. Но теперь, когда у меня есть рабочая функция, я собираюсь реализовать поблочный метод, так что можно обеспечить итерабельность блоков для шифрования или дешифрования и иметь возможность сделать это, не имея всего источника и назначения все в памяти все сразу - таким образом, можно было бы зашифровать/дешифровать огромные файлы, не беспокоясь о том, чтобы удерживать до трех копий в памяти в любой точке ...
Спасибо всем за помощь!
... вот почему я, вероятно, должен был пойти спать прошлой ночью, потому что я пытался запрограммировать, пока переутомил. В самом деле, что заставило его работать, был вызов strncpy (который я использовал из-за возможности NULL-байтов на входе), а затем я смог выполнить вызов mcrypt_generic, скопировать вывод в Python, временный буфер и возврат. Спасибо за этот ответ, он указал мне в правильном направлении. –
!!! 'strncpy' будет ** не ** помочь вам, если на входе могут быть NULL байты. Это означает, что ваш ввод не является действительно строкой, а последовательностью байтов. Используйте 'memcpy' или что-то в этом роде. –
О, вы абсолютно правы; n - «до». О, дерьмо. О, о, дерьмо. Я думаю, вы могли бы просто указать мою ошибку, которую я опубликовал по другому вопросу: http://stackoverflow.com/questions/4451977/data-corruption-wheres-the-bug –