From 455df9779873b8335b20292b8d0c43d66338a4db Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 15 Apr 2020 14:05:24 +0200 Subject: [PATCH] Optimize _Py_strhex_impl() (GH-19535) Avoid a temporary buffer to create a bytes string: use PyBytes_FromStringAndSize() to directly allocate a bytes object. Cleanup also the code: PEP 7 formatting, move variable definitions closer to where they are used. Fix assertion checking "j" index. --- Python/pystrhex.c | 52 ++++++++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/Python/pystrhex.c b/Python/pystrhex.c index 9d34f71a2e9..7e4fad30320 100644 --- a/Python/pystrhex.c +++ b/Python/pystrhex.c @@ -8,12 +8,9 @@ static PyObject *_Py_strhex_impl(const char* argbuf, const Py_ssize_t arglen, const PyObject* sep, int bytes_per_sep_group, const int return_bytes) { - PyObject *retval; - Py_UCS1* retbuf; - Py_ssize_t i, j, resultlen = 0; - Py_UCS1 sep_char = 0; - unsigned int abs_bytes_per_sep; + assert(arglen >= 0); + Py_UCS1 sep_char = 0; if (sep) { Py_ssize_t seplen = PyObject_Length((PyObject*)sep); if (seplen < 0) { @@ -31,9 +28,11 @@ static PyObject *_Py_strhex_impl(const char* argbuf, const Py_ssize_t arglen, return NULL; } sep_char = PyUnicode_READ_CHAR(sep, 0); - } else if (PyBytes_Check(sep)) { + } + else if (PyBytes_Check(sep)) { sep_char = PyBytes_AS_STRING(sep)[0]; - } else { + } + else { PyErr_SetString(PyExc_TypeError, "sep must be str or bytes."); return NULL; } @@ -41,12 +40,13 @@ static PyObject *_Py_strhex_impl(const char* argbuf, const Py_ssize_t arglen, PyErr_SetString(PyExc_ValueError, "sep must be ASCII."); return NULL; } - } else { + } + else { bytes_per_sep_group = 0; } - assert(arglen >= 0); - abs_bytes_per_sep = abs(bytes_per_sep_group); + unsigned int abs_bytes_per_sep = abs(bytes_per_sep_group); + Py_ssize_t resultlen = 0; if (bytes_per_sep_group && arglen > 0) { /* How many sep characters we'll be inserting. */ resultlen = (arglen - 1) / abs_bytes_per_sep; @@ -62,26 +62,32 @@ static PyObject *_Py_strhex_impl(const char* argbuf, const Py_ssize_t arglen, abs_bytes_per_sep = 0; } + PyObject *retval; + Py_UCS1 *retbuf; if (return_bytes) { /* If _PyBytes_FromSize() were public we could avoid malloc+copy. */ - retbuf = (Py_UCS1*) PyMem_Malloc(resultlen); - if (!retbuf) - return PyErr_NoMemory(); - retval = NULL; /* silence a compiler warning, assigned later. */ - } else { - retval = PyUnicode_New(resultlen, 127); - if (!retval) + retval = PyBytes_FromStringAndSize(NULL, resultlen); + if (!retval) { return NULL; + } + retbuf = (Py_UCS1 *)PyBytes_AS_STRING(retval); + } + else { + retval = PyUnicode_New(resultlen, 127); + if (!retval) { + return NULL; + } retbuf = PyUnicode_1BYTE_DATA(retval); } /* Hexlify */ + Py_ssize_t i, j; for (i=j=0; i < arglen; ++i) { - assert(j < resultlen); + assert((j + 1) < resultlen); unsigned char c; - c = (argbuf[i] >> 4) & 0xf; + c = (argbuf[i] >> 4) & 0x0f; retbuf[j++] = Py_hexdigits[c]; - c = argbuf[i] & 0xf; + c = argbuf[i] & 0x0f; retbuf[j++] = Py_hexdigits[c]; if (bytes_per_sep_group && i < arglen - 1) { Py_ssize_t anchor; @@ -93,12 +99,8 @@ static PyObject *_Py_strhex_impl(const char* argbuf, const Py_ssize_t arglen, } assert(j == resultlen); - if (return_bytes) { - retval = PyBytes_FromStringAndSize((const char *)retbuf, resultlen); - PyMem_Free(retbuf); - } #ifdef Py_DEBUG - else { + if (!return_bytes) { assert(_PyUnicode_CheckConsistency(retval, 1)); } #endif