Skip to content

Commit 455df97

Browse files
authored
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.
1 parent a86b522 commit 455df97

File tree

1 file changed

+26
-24
lines changed

1 file changed

+26
-24
lines changed

Python/pystrhex.c

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,9 @@ static PyObject *_Py_strhex_impl(const char* argbuf, const Py_ssize_t arglen,
88
const PyObject* sep, int bytes_per_sep_group,
99
const int return_bytes)
1010
{
11-
PyObject *retval;
12-
Py_UCS1* retbuf;
13-
Py_ssize_t i, j, resultlen = 0;
14-
Py_UCS1 sep_char = 0;
15-
unsigned int abs_bytes_per_sep;
11+
assert(arglen >= 0);
1612

13+
Py_UCS1 sep_char = 0;
1714
if (sep) {
1815
Py_ssize_t seplen = PyObject_Length((PyObject*)sep);
1916
if (seplen < 0) {
@@ -31,22 +28,25 @@ static PyObject *_Py_strhex_impl(const char* argbuf, const Py_ssize_t arglen,
3128
return NULL;
3229
}
3330
sep_char = PyUnicode_READ_CHAR(sep, 0);
34-
} else if (PyBytes_Check(sep)) {
31+
}
32+
else if (PyBytes_Check(sep)) {
3533
sep_char = PyBytes_AS_STRING(sep)[0];
36-
} else {
34+
}
35+
else {
3736
PyErr_SetString(PyExc_TypeError, "sep must be str or bytes.");
3837
return NULL;
3938
}
4039
if (sep_char > 127 && !return_bytes) {
4140
PyErr_SetString(PyExc_ValueError, "sep must be ASCII.");
4241
return NULL;
4342
}
44-
} else {
43+
}
44+
else {
4545
bytes_per_sep_group = 0;
4646
}
4747

48-
assert(arglen >= 0);
49-
abs_bytes_per_sep = abs(bytes_per_sep_group);
48+
unsigned int abs_bytes_per_sep = abs(bytes_per_sep_group);
49+
Py_ssize_t resultlen = 0;
5050
if (bytes_per_sep_group && arglen > 0) {
5151
/* How many sep characters we'll be inserting. */
5252
resultlen = (arglen - 1) / abs_bytes_per_sep;
@@ -62,26 +62,32 @@ static PyObject *_Py_strhex_impl(const char* argbuf, const Py_ssize_t arglen,
6262
abs_bytes_per_sep = 0;
6363
}
6464

65+
PyObject *retval;
66+
Py_UCS1 *retbuf;
6567
if (return_bytes) {
6668
/* If _PyBytes_FromSize() were public we could avoid malloc+copy. */
67-
retbuf = (Py_UCS1*) PyMem_Malloc(resultlen);
68-
if (!retbuf)
69-
return PyErr_NoMemory();
70-
retval = NULL; /* silence a compiler warning, assigned later. */
71-
} else {
69+
retval = PyBytes_FromStringAndSize(NULL, resultlen);
70+
if (!retval) {
71+
return NULL;
72+
}
73+
retbuf = (Py_UCS1 *)PyBytes_AS_STRING(retval);
74+
}
75+
else {
7276
retval = PyUnicode_New(resultlen, 127);
73-
if (!retval)
77+
if (!retval) {
7478
return NULL;
79+
}
7580
retbuf = PyUnicode_1BYTE_DATA(retval);
7681
}
7782

7883
/* Hexlify */
84+
Py_ssize_t i, j;
7985
for (i=j=0; i < arglen; ++i) {
80-
assert(j < resultlen);
86+
assert((j + 1) < resultlen);
8187
unsigned char c;
82-
c = (argbuf[i] >> 4) & 0xf;
88+
c = (argbuf[i] >> 4) & 0x0f;
8389
retbuf[j++] = Py_hexdigits[c];
84-
c = argbuf[i] & 0xf;
90+
c = argbuf[i] & 0x0f;
8591
retbuf[j++] = Py_hexdigits[c];
8692
if (bytes_per_sep_group && i < arglen - 1) {
8793
Py_ssize_t anchor;
@@ -93,12 +99,8 @@ static PyObject *_Py_strhex_impl(const char* argbuf, const Py_ssize_t arglen,
9399
}
94100
assert(j == resultlen);
95101

96-
if (return_bytes) {
97-
retval = PyBytes_FromStringAndSize((const char *)retbuf, resultlen);
98-
PyMem_Free(retbuf);
99-
}
100102
#ifdef Py_DEBUG
101-
else {
103+
if (!return_bytes) {
102104
assert(_PyUnicode_CheckConsistency(retval, 1));
103105
}
104106
#endif

0 commit comments

Comments
 (0)