J'essaye d'écrire une extension Cython à CPython pour envelopper la bibliothèque mcrypt, de sorte que je puisse l'utiliser avec Python 3. Cependant, je rencontre un problème où je me sépare en essayant d'utiliser une des API mcrypt.Cython octets à C char *
Le code qui échoue est:
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
Maintenant, la façon dont je comprends la documentation Cython, l'affectation de la ligne 3 devrait copier le contenu de la mémoire tampon (un objet en Python 3) à la C pointeur de chaîne. Je comprendre que cela signifie également qu'il attribuerait la mémoire, mais quand je fait cette modification:
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
il est écrasé encore avec un segfault. Il se bloque à l'intérieur de mcrypt_generic, mais quand j'utilise du code C simple, je suis capable de le faire fonctionner correctement, donc il doit y avoir quelque chose que je ne comprends pas bien comment Cython fonctionne avec les données C ici.
Merci pour toute aide!
ETA: Le problème était un bug de ma part. Je travaillais là-dessus après avoir été réveillé pendant trop d'heures (n'est-ce pas quelque chose que nous avons tous fait à un moment donné?) Et j'ai raté quelque chose de stupide. Le code que j'ai maintenant, ce qui fonctionne, est:
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
Il est probablement pas le code le plus efficace dans le monde, car il fait une copie pour faire le chiffrement, puis une deuxième copie à la valeur de retour. Je ne suis pas sûr si c'est possible d'éviter cela, cependant, puisque je ne suis pas sûr s'il est possible de prendre un tampon nouvellement alloué et le renvoyer à Python in-place comme un bytestring. Mais maintenant que j'ai une fonction de travail, je vais également implémenter une méthode block-by-block, de sorte que l'on puisse fournir un itérable de blocs pour le chiffrement ou le décryptage, et pouvoir le faire sans avoir la source entière et la destination tout dans la mémoire tout à la fois --- de cette façon, il serait possible de crypter/décrypter des fichiers énormes sans avoir à en garder jusqu'à trois copies en mémoire à un moment donné ...
Merci à tous pour l'aide!
... ce serait pourquoi je devrais probablement être allé au lit la nuit dernière, parce que j'essayais de programmer tout fatigué. En effet, ce qui l'a fait fonctionner était un appel à strncpy (que j'ai utilisé en raison de la possibilité de NULL octets dans l'entrée), et puis je pouvais faire l'appel à mcrypt_generic, copier la sortie dans un bytestring Python, libérer le tampon temporaire et retour. Merci pour cette réponse, elle m'a indiqué dans la bonne direction. –
!!! 'strncpy' ne ** vous ** aidera pas s'il peut valablement y avoir des octets NULL dans l'entrée. Cela signifie que votre entrée n'est pas vraiment une chaîne du tout, mais une séquence d'octets. Utilisez 'memcpy' ou quelque chose comme ça. –
Oh, vous avez absolument raison; le n est "jusqu'à". Oh, merde. Oh, oh, merde. Je pense que vous pourriez avoir juste signalé mon bogue que j'ai posté sur une autre question: http://stackoverflow.com/questions/4451977/data-corruption-wheres-the-bug –