Index: Modules/_hashopenssl.c =================================================================== --- Modules/_hashopenssl.c (Revision 69545) +++ Modules/_hashopenssl.c (Arbeitskopie) @@ -21,15 +21,61 @@ #define MUNCH_SIZE INT_MAX - #ifndef HASH_OBJ_CONSTRUCTOR #define HASH_OBJ_CONSTRUCTOR 0 #endif +#ifdef WITH_THREAD + #include "pythread.h" + + #define ENTER_HASHLIB(obj) \ + if ((obj)->lock) { \ + if (!PyThread_acquire_lock((obj)->lock, 0)) { \ + Py_BEGIN_ALLOW_THREADS \ + PyThread_acquire_lock((obj)->lock, 1); \ + Py_END_ALLOW_THREADS \ + } \ + } + #define LEAVE_HASHLIB(obj) \ + if ((obj)->lock) { \ + PyThread_release_lock((obj)->lock); \ + } +#else + #define ENTER_HASHLIB(obj) + #define LEAVE_HASHLIB(obj) +#endif + +#define GET_BUFFER_VIEW_OR_ERROUT(obj, viewp) do { \ + if (PyUnicode_Check((obj))) { \ + PyErr_SetString(PyExc_TypeError, \ + "Unicode-objects must be encoded before hashing");\ + return NULL; \ + } \ + if (!PyObject_CheckBuffer((obj))) { \ + PyErr_SetString(PyExc_TypeError, \ + "object supporting the buffer API required"); \ + return NULL; \ + } \ + if (PyObject_GetBuffer((obj), (viewp), PyBUF_SIMPLE) == -1) { \ + return NULL; \ + } \ + if ((viewp)->ndim > 1) { \ + PyErr_SetString(PyExc_BufferError, \ + "Buffer must be single dimension"); \ + PyBuffer_Release((viewp)); \ + return NULL; \ + } \ + } while(0); + +#define HASHLIB_GIL_MINSIZE 2048 + typedef struct { PyObject_HEAD PyObject *name; /* name of this hash algorithm */ EVP_MD_CTX ctx; /* OpenSSL message digest context */ +#ifdef WITH_THREAD + PyThread_type_lock lock; /* OpenSSL context lock */ +#endif } EVPobject; @@ -58,19 +104,42 @@ if (retval != NULL) { Py_INCREF(name); retval->name = name; +#ifdef WITH_THREAD + retval->lock = NULL; +#endif } return retval; } +static void +EVP_hash(EVPobject *self, const void *vp, Py_ssize_t len) +{ + unsigned int process; + const unsigned char *cp = (const unsigned char *)vp; + while (0 < len) { + if (len > (Py_ssize_t)MUNCH_SIZE) + process = MUNCH_SIZE; + else + process = Py_SAFE_DOWNCAST(len, Py_ssize_t, unsigned int); + EVP_DigestUpdate(&self->ctx, (const void*)cp, process); + len -= process; + cp += process; + } +} + /* Internal methods for a hash object */ static void -EVP_dealloc(PyObject *ptr) +EVP_dealloc(EVPobject *self) { - EVP_MD_CTX_cleanup(&((EVPobject *)ptr)->ctx); - Py_XDECREF(((EVPobject *)ptr)->name); - PyObject_Del(ptr); +#ifdef WITH_THREAD + if (self->lock != NULL) + PyThread_free_lock(self->lock); +#endif + EVP_MD_CTX_cleanup(&((EVPobject *)self)->ctx); + Py_XDECREF(((EVPobject *)self)->name); + PyObject_Del(self); } @@ -86,7 +155,9 @@ if ( (newobj = newEVPobject(self->name))==NULL) return NULL; + ENTER_HASHLIB(self); EVP_MD_CTX_copy(&newobj->ctx, &self->ctx); + LEAVE_HASHLIB(self); return (PyObject *)newobj; } @@ -101,7 +172,9 @@ PyObject *retval; unsigned int digest_size; + ENTER_HASHLIB(self); EVP_MD_CTX_copy(&temp_ctx, &self->ctx); + LEAVE_HASHLIB(self); digest_size = EVP_MD_CTX_size(&temp_ctx); EVP_DigestFinal(&temp_ctx, digest, NULL); @@ -123,7 +196,9 @@ unsigned int i, j, digest_size; /* Get the raw (binary) digest value */ + ENTER_HASHLIB(self); EVP_MD_CTX_copy(&temp_ctx, &self->ctx); + LEAVE_HASHLIB(self); digest_size = EVP_MD_CTX_size(&temp_ctx); EVP_DigestFinal(&temp_ctx, digest, NULL); @@ -160,24 +235,39 @@ static PyObject * EVP_update(EVPobject *self, PyObject *args) { - unsigned char *cp; - Py_ssize_t len; + PyObject *obj; + Py_buffer view; - if (!PyArg_ParseTuple(args, "s#:update", &cp, &len)) + if (!PyArg_ParseTuple(args, "O:update", &obj)) return NULL; - if (len > 0 && len <= MUNCH_SIZE) { - EVP_DigestUpdate(&self->ctx, cp, Py_SAFE_DOWNCAST(len, Py_ssize_t, - unsigned int)); - } else { - Py_ssize_t offset = 0; - while (len) { - unsigned int process = len > MUNCH_SIZE ? MUNCH_SIZE : len; - EVP_DigestUpdate(&self->ctx, cp + offset, process); - len -= process; - offset += process; + GET_BUFFER_VIEW_OR_ERROUT(obj, &view); + +#ifdef WITH_THREAD + if (self->lock == NULL && view.len >= HASHLIB_GIL_MINSIZE) { + self->lock = PyThread_allocate_lock(); + if (self->lock == NULL) { + PyBuffer_Release(&view); + PyErr_SetString(PyExc_MemoryError, "unable to allocate lock"); + return NULL; } } + + if (self->lock != NULL) { + Py_BEGIN_ALLOW_THREADS + PyThread_acquire_lock(self->lock, 1); + EVP_hash(self, view.buf, view.len); + PyThread_release_lock(self->lock); + Py_END_ALLOW_THREADS + } else { + EVP_hash(self, view.buf, view.len); + } +#else + EVP_hash(self, view.buf, view.len); +#endif + + PyBuffer_Release(&view); + Py_INCREF(Py_None); return Py_None; } @@ -193,13 +283,21 @@ static PyObject * EVP_get_block_size(EVPobject *self, void *closure) { - return PyInt_FromLong(EVP_MD_CTX_block_size(&((EVPobject *)self)->ctx)); + long block_size; + ENTER_HASHLIB(self); + block_size = EVP_MD_CTX_block_size(&self->ctx); + LEAVE_HASHLIB(self); + return PyLong_FromLong(block_size); } static PyObject * EVP_get_digest_size(EVPobject *self, void *closure) { - return PyInt_FromLong(EVP_MD_CTX_size(&((EVPobject *)self)->ctx)); + long size; + ENTER_HASHLIB(self); + size = EVP_MD_CTX_size(&self->ctx); + LEAVE_HASHLIB(self); + return PyLong_FromLong(size); } static PyMemberDef EVP_members[] = { @@ -241,21 +339,26 @@ { static char *kwlist[] = {"name", "string", NULL}; PyObject *name_obj = NULL; + PyObject *data_obj = NULL; + Py_buffer view; char *nameStr; - unsigned char *cp = NULL; - Py_ssize_t len = 0; const EVP_MD *digest; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|s#:HASH", kwlist, - &name_obj, &cp, &len)) { + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O:HASH", kwlist, + &name_obj, &data_obj)) { return -1; } if (!PyArg_Parse(name_obj, "s", &nameStr)) { PyErr_SetString(PyExc_TypeError, "name must be a string"); + if (data_obj) + PyBuffer_Release(&view); return -1; } + if (data_obj) + GET_BUFFER_VIEW_OR_ERROUT(data_obj, &view); + digest = EVP_get_digestbyname(nameStr); if (!digest) { PyErr_SetString(PyExc_ValueError, "unknown hash function"); @@ -266,19 +369,15 @@ self->name = name_obj; Py_INCREF(self->name); - if (cp && len) { - if (len > 0 && len <= MUNCH_SIZE) { - EVP_DigestUpdate(&self->ctx, cp, Py_SAFE_DOWNCAST(len, Py_ssize_t, - unsigned int)); + if (data_obj) { + if (view.len >= HASHLIB_GIL_MINSIZE) { + Py_BEGIN_ALLOW_THREADS + EVP_hash(self, view.buf, view.len); + Py_END_ALLOW_THREADS } else { - Py_ssize_t offset = 0; - while (len) { - unsigned int process = len > MUNCH_SIZE ? MUNCH_SIZE : len; - EVP_DigestUpdate(&self->ctx, cp + offset, process); - len -= process; - offset += process; - } + EVP_hash(self, view.buf, view.len); } + PyBuffer_Release(&view); } return 0; @@ -305,15 +404,15 @@ static PyTypeObject EVPtype = { PyVarObject_HEAD_INIT(NULL, 0) "_hashlib.HASH", /*tp_name*/ - sizeof(EVPobject), /*tp_basicsize*/ - 0, /*tp_itemsize*/ + sizeof(EVPobject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ /* methods */ - EVP_dealloc, /*tp_dealloc*/ - 0, /*tp_print*/ + (destructor)EVP_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ - EVP_repr, /*tp_repr*/ + (reprfunc)EVP_repr, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ @@ -368,17 +467,12 @@ } if (cp && len) { - if (len > 0 && len <= MUNCH_SIZE) { - EVP_DigestUpdate(&self->ctx, cp, Py_SAFE_DOWNCAST(len, Py_ssize_t, - unsigned int)); + if (len >= HASHLIB_GIL_MINSIZE) { + Py_BEGIN_ALLOW_THREADS + EVP_hash(self, cp, len); + Py_END_ALLOW_THREADS } else { - Py_ssize_t offset = 0; - while (len) { - unsigned int process = len > MUNCH_SIZE ? MUNCH_SIZE : len; - EVP_DigestUpdate(&self->ctx, cp + offset, process); - len -= process; - offset += process; - } + EVP_hash(self, cp, len); } } @@ -399,14 +493,14 @@ EVP_new(PyObject *self, PyObject *args, PyObject *kwdict) { static char *kwlist[] = {"name", "string", NULL}; - PyObject *name_obj = NULL; + PyObject *name_obj, *ret_obj; + PyObject *data_obj = NULL; + Py_buffer view; char *name; const EVP_MD *digest; - unsigned char *cp = NULL; - Py_ssize_t len = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwdict, "O|s#:new", kwlist, - &name_obj, &cp, &len)) { + if (!PyArg_ParseTupleAndKeywords(args, kwdict, "O|O:new", kwlist, + &name_obj, &data_obj)) { return NULL; } @@ -415,9 +509,16 @@ return NULL; } + if (data_obj) + GET_BUFFER_VIEW_OR_ERROUT(data_obj, &view); + digest = EVP_get_digestbyname(name); - return EVPnew(name_obj, digest, NULL, cp, len); + ret_obj = EVPnew(name_obj, digest, NULL, (unsigned char*)view.buf, view.len); + + if (data_obj) + PyBuffer_Release(&view); + return ret_obj; } /* @@ -431,18 +532,24 @@ static PyObject * \ EVP_new_ ## NAME (PyObject *self, PyObject *args) \ { \ - unsigned char *cp = NULL; \ - Py_ssize_t len = 0; \ + PyObject *data_obj = NULL; \ + Py_buffer view = { 0 }; \ + PyObject *ret_obj; \ \ - if (!PyArg_ParseTuple(args, "|s#:" #NAME , &cp, &len)) { \ + if (!PyArg_ParseTuple(args, "|O:" #NAME , &data_obj)) \ return NULL; \ - } \ + if (data_obj) \ + GET_BUFFER_VIEW_OR_ERROUT(data_obj, &view); \ \ return EVPnew( \ CONST_ ## NAME ## _name_obj, \ NULL, \ CONST_new_ ## NAME ## _ctx_p, \ - cp, len); \ + (unsigned char*)view.buf, view.len); \ + \ + if (data_obj) \ + PyBuffer_Release(&view); \ + return ret_obj; \ } /* a PyMethodDef structure for the constructor */