Cython байтов в C char * - PullRequest
       25

Cython байтов в C char *

4 голосов
/ 14 декабря 2010

Я пытаюсь написать расширение Cython для CPython, чтобы обернуть библиотеку mcrypt, чтобы я мог использовать ее с Python 3. Однако я сталкиваюсь с проблемой, когда у меня возникает ошибка при попытке использовать один из API mcrypt.

Код, который не работает:

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

он все еще падал с ошибкой. Он выходит из строя внутри 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 на месте в качестве строки байтов. Но теперь, когда у меня есть рабочая функция, я собираюсь также реализовать блочный метод, чтобы можно было предоставлять итерируемые блоки для шифрования или дешифрования, и иметь возможность делать это, не имея всего источника и назначение целиком в памяти все сразу - таким образом, было бы возможно зашифровать / расшифровать огромные файлы, не беспокоясь о том, чтобы хранить до трех копий в памяти в любой точке ...

Спасибо за помощь, всем!

Ответы [ 3 ]

4 голосов
/ 14 декабря 2010

Первый указывает char* на строку Python. Второй выделяет память, но затем повторно указывает указатель на строку Python и игнорирует вновь выделенную память. Вы должны вызывать библиотечную функцию C strcpy из Cython, предположительно; но я не знаю деталей.

3 голосов
/ 21 февраля 2011

Несколько комментариев к вашему коду, чтобы помочь улучшить его, ИМХО.Существуют функции, предоставляемые 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)

Приведенные выше строкиновый объект Python str, инициализированный содержимым source.Вторая строка указывает ciphertext на retval внутренний буфер char * без копирования.Все, что модифицирует ciphertext, изменит retval.Поскольку retval является новой строкой Python, ее можно изменить с помощью кода C перед возвратом из _real_encrypt.

Дополнительные сведения см. В документации по 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
2 голосов
/ 14 декабря 2010

Подход, который я использовал (с Python 2.x), заключается в объявлении параметров строкового типа в сигнатуре функции, чтобы код Cython выполнял все преобразования и проверку типов автоматически:

def _real_encrypt(self,char* src):
    ...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...