From ac3c5c84254ad73f8157c22a074cfb069a30cdaa Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Fri, 16 Sep 2011 14:22:32 -0400 Subject: [PATCH 1/4] Rework _hashlib caching, moving per-hash cached data into a struct Introduce a EVPConstructorCache type into Modules/_hashlib.c, replacing: CONST_ ## Name ## _name_obj with cached_info->name_obj likewise, replacing EVP_MD_CTX CONST_new_ ## Name ## _ctx with cached_info->ctx and EVP_MD_CTX *CONST_new_ ## Name ## _ctx_p with cached_info->ctx_ptr --- Modules/_hashopenssl.c | 99 +++++++++++++++++++++++++++++++----------------- 1 files changed, 64 insertions(+), 35 deletions(-) diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c index dd4317f..f921a17 100644 --- a/Modules/_hashopenssl.c +++ b/Modules/_hashopenssl.c @@ -68,11 +68,20 @@ typedef struct { static PyTypeObject EVPtype; +/* Struct to hold all the cached information we need on a specific algorithm. + We have one of these per algorithm, copying the ctx to amortize the startup + cost of the constructor. */ +typedef struct { + PyObject *name_obj; + + EVP_MD_CTX ctx; -#define DEFINE_CONSTS_FOR_NEW(Name) \ - static PyObject *CONST_ ## Name ## _name_obj; \ - static EVP_MD_CTX CONST_new_ ## Name ## _ctx; \ - static EVP_MD_CTX *CONST_new_ ## Name ## _ctx_p = NULL; + /* ctx_ptr will point to ctx above */ + EVP_MD_CTX *ctx_ptr; +} EVPConstructorCache; + +#define DEFINE_CONSTS_FOR_NEW(Name) \ + static EVPConstructorCache cached_info_ ##Name; DEFINE_CONSTS_FOR_NEW(md5) DEFINE_CONSTS_FOR_NEW(sha1) @@ -547,39 +556,51 @@ generate_hash_name_list(void) /* - * This macro generates constructor function definitions for specific - * hash algorithms. These constructors are much faster than calling - * the generic one passing it a python string and are noticably - * faster than calling a python new() wrapper. Thats important for + * This macro and function generates a family of constructor function + * definitions for specific hash algorithms. These constructors are much + * faster than calling the generic one passing it a python string and are + * noticably faster than calling a python new() wrapper. That's important for * code that wants to make hashes of a bunch of small strings. */ #define GEN_CONSTRUCTOR(NAME) \ static PyObject * \ EVP_new_ ## NAME (PyObject *self, PyObject *args) \ { \ - PyObject *data_obj = NULL; \ - Py_buffer view = { 0 }; \ - PyObject *ret_obj; \ - \ - if (!PyArg_ParseTuple(args, "|O:" #NAME , &data_obj)) { \ - return NULL; \ - } \ - \ - if (data_obj) \ - GET_BUFFER_VIEW_OR_ERROUT(data_obj, &view); \ - \ - ret_obj = EVPnew( \ - CONST_ ## NAME ## _name_obj, \ - NULL, \ - CONST_new_ ## NAME ## _ctx_p, \ - (unsigned char*)view.buf, \ - view.len); \ - \ - if (data_obj) \ - PyBuffer_Release(&view); \ - return ret_obj; \ + return implement_specific_EVP_new(self, args, \ + "|O:" #NAME, \ + &cached_info_ ## NAME ); \ } +static PyObject * +implement_specific_EVP_new(PyObject *self, PyObject *args, + const char *format, + EVPConstructorCache *cached_info) +{ + PyObject *data_obj = NULL; + Py_buffer view = { 0 }; + PyObject *ret_obj = NULL; + + assert(cached_info); + + if (!PyArg_ParseTuple(args, format, &data_obj)) { + return NULL; + } + + if (data_obj) + GET_BUFFER_VIEW_OR_ERROUT(data_obj, &view); + + ret_obj = EVPnew(cached_info->name_obj, + NULL, + cached_info->ctx_ptr, + (unsigned char*)view.buf, + view.len); + + if (data_obj) + PyBuffer_Release(&view); + + return ret_obj; +} + /* a PyMethodDef structure for the constructor */ #define CONSTRUCTOR_METH_DEF(NAME) \ {"openssl_" #NAME, (PyCFunction)EVP_new_ ## NAME, METH_VARARGS, \ @@ -588,14 +609,22 @@ generate_hash_name_list(void) } /* used in the init function to setup a constructor */ -#define INIT_CONSTRUCTOR_CONSTANTS(NAME) do { \ - CONST_ ## NAME ## _name_obj = PyUnicode_FromString(#NAME); \ - if (EVP_get_digestbyname(#NAME)) { \ - CONST_new_ ## NAME ## _ctx_p = &CONST_new_ ## NAME ## _ctx; \ - EVP_DigestInit(CONST_new_ ## NAME ## _ctx_p, EVP_get_digestbyname(#NAME)); \ - } \ +#define INIT_CONSTRUCTOR_CONSTANTS(NAME) do { \ + init_constructor_constant(&cached_info_ ## NAME, #NAME); \ } while (0); +static void +init_constructor_constant(EVPConstructorCache *cached_info, const char *name) +{ + assert(cached_info); + cached_info->name_obj = PyUnicode_FromString(name); + if (EVP_get_digestbyname(name)) { + cached_info->ctx_ptr = &cached_info->ctx; + EVP_DigestInit(&cached_info->ctx, + EVP_get_digestbyname(name)); + } +} + GEN_CONSTRUCTOR(md5) GEN_CONSTRUCTOR(sha1) #ifdef _OPENSSL_SUPPORTS_SHA2 -- 1.7.6