An alternative approach is to use PyObject_GetBuffer . The module below defines fast_xor for any object that supports the buffer protocol, and fast_xor_inplace for objects with rewritable buffers, such as bytearray . This version returns None . I also added a second unsigned char argument with a default value of 55.
Example:
>>> s = 'abc' >>> b = bytearray(s) >>> fast_xor(s), fast_xor(s, 0x20) ('VUT', 'ABC') >>> fast_xor_inplace(b, 0x20) >>> b bytearray(b'ABC') >>> fast_xor_inplace(s) Traceback (most recent call last): File "<stdin>", line 1, in <module> BufferError: Object is not writable. >>> fast_xor(b, 256) Traceback (most recent call last): File "<stdin>", line 1, in <module> OverflowError: unsigned byte integer is greater than maximum
A source:
#include <Python.h> static PyObject *fast_xor_inplace(PyObject *self, PyObject *args) { PyObject *arg1; unsigned char arg2 = 55; Py_buffer buffer; char *buf; int i; if (!PyArg_ParseTuple(args, "O|b:fast_xor_inplace", &arg1, &arg2)) return NULL; if (PyObject_GetBuffer(arg1, &buffer, PyBUF_WRITABLE) < 0) return NULL; buf = buffer.buf; for(i=0; i < buffer.len; i++) buf[i] ^= arg2; PyBuffer_Release(&buffer); Py_INCREF(Py_None); return Py_None; } static PyObject *fast_xor(PyObject *self, PyObject *args) { PyObject *arg1; unsigned char arg2 = 55; PyObject *result; Py_buffer buffer; char *buf, *str; int i; if (!PyArg_ParseTuple(args, "O|b:fast_xor", &arg1, &arg2)) return NULL; if (PyObject_GetBuffer(arg1, &buffer, PyBUF_SIMPLE) < 0) return NULL; result = PyString_FromStringAndSize(NULL, buffer.len); if (result == NULL) return NULL; buf = buffer.buf; str = PyString_AS_STRING(result); for(i=0; i < buffer.len; i++) str[i] = buf[i] ^ arg2; PyBuffer_Release(&buffer); return result; } static PyMethodDef fastxorMethods[] = { {"fast_xor", fast_xor, METH_VARARGS, "fast xor"}, {"fast_xor_inplace", fast_xor_inplace, METH_VARARGS, "fast inplace xor"}, {NULL, NULL, 0, NULL} }; PyMODINIT_FUNC initfastxor(void) { Py_InitModule3("fastxor", fastxorMethods, "fast xor functions"); }